CSU 1811: Tree Intersection(线段树启发式合并||map启发式合并)
http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1811
题意:给出一棵树,每一个结点有一个颜色,然后依次删除树边,问每次删除树边之后,分开的两个连通块里面的颜色交集数是多少,即公有的颜色数。
思路:可以像树形DP一样,先处理出儿子结点,然后回溯的时候和儿子结点的子树合并,更新父结点的子树,然后更新父结点的答案。
枚举删除的边。以u为子树里面放的是某个颜色有多少,然后和一开始统计的某种颜色的总数比较,如果树里面这个颜色的数目小于这个颜色的总数,那么这个颜色就肯定有一些是在另一个连通块里面,那么就是公有的,对答案有贡献。
如果树里面这个颜色的数目为0或者等于这个颜色的总数,说明不是公有的,那么对答案就没贡献。
因为直接合并的话O(n^2)的复杂度太大,因此用启发式合并达到O(nlogn)。
写了两种方法,都很好理解。
线段树:空间不足,因此要动态开辟结点
#include <bits/stdc++.h>
using namespace std;
#define N 100010
struct node {
int val, cnt, l, r; // val是颜色c的个数,sum是答案的个数
} tree[N*];
struct Edge {
int v, nxt, id;
} edge[N*];
int n, col[N], sum[N], head[N], tot, sz, root[N], ans[N], e[N];
// 共有的就是交集就是答案
void Add(int u, int v, int id) {
edge[tot] = (Edge) { v, head[u], id }; head[u] = tot++;
edge[tot] = (Edge) { u, head[v], id }; head[v] = tot++;
} void PushUp(int now) {
tree[now].cnt = tree[tree[now].l].cnt + tree[tree[now].r].cnt;
} int Build(int l, int r, int c) {
int now = ++sz;
tree[now].l = tree[now].r = ;
int m = (l + r) >> ;
if(l == r) {
tree[now].val = ;
tree[now].cnt = (tree[now].val < sum[c] ? : );
return now;
}
if(c <= m) tree[now].l = Build(l, m, c);
else tree[now].r = Build(m + , r, c);
PushUp(now);
return now;
} void Merge(int &rt1, int rt2, int l, int r) {
if(!rt1 || !rt2) {
if(!rt1) rt1 = rt2; // 小的变成大的
return ;
}
if(l == r) {
tree[rt1].val += tree[rt2].val;
tree[rt1].cnt = (tree[rt1].val < sum[l] ? : );
return ;
}
int m = (l + r) >> ;
Merge(tree[rt1].l, tree[rt2].l, l, m);
Merge(tree[rt1].r, tree[rt2].r, m + , r);
PushUp(rt1);
} void DFS(int u, int fa, int id) {
root[u] = Build(, n, col[u]);
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(v == fa) continue;
DFS(v, u, edge[i].id);
Merge(root[u], root[v], , n);
}
if(id) e[id] = tree[root[u]].cnt;
} int main() {
while(~scanf("%d", &n)) {
memset(sum, , sizeof(sum));
for(int i = ; i <= n; i++) scanf("%d", &col[i]), sum[col[i]]++;
memset(head, -, sizeof(head)); sz = , tot = ;
for(int i = ; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
Add(u, v, i);
}
DFS(, -, );
for(int i = ; i < n; i++)
printf("%d\n", e[i]);
}
return ;
}
map:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
struct Edge {
int v, nxt, id;
} edge[N*];
map<int, int> num[N];
int n, col[N], sum[N], cnt[N], e[N], head[N], tot; void Add(int u, int v, int id) {
edge[tot] = (Edge) { v, head[u], id }; head[u] = tot++;
edge[tot] = (Edge) { u, head[v], id }; head[v] = tot++;
} void DFS(int u, int fa, int id) {
num[u][col[u]] = ; cnt[u] = num[u][col[u]] < sum[col[u]] ? : ;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v, idd = edge[i].id;
if(v == fa) continue;
DFS(v, u, idd);
if(num[u].size() < num[v].size()) // 启发式合并
swap(num[u], num[v]), swap(cnt[u], cnt[v]);
for(map<int,int>::iterator it = num[v].begin(); it != num[v].end(); it++) {
int key = it->first, cc = it->second;
if(num[u][key] + cc < sum[key] && num[u][key] == ) cnt[u]++; // 如果之前没被算过,并且是共有的就要加上
if(num[u][key] + cc == sum[key] && num[u][key] > ) cnt[u]--; // 如果之前被算过,并且是特有的就要减去
num[u][key] += cc;
}
}
if(id) e[id] = cnt[u];
} int main() {
while(~scanf("%d", &n)) {
memset(sum, , sizeof(sum));
memset(cnt, , sizeof(cnt));
for(int i = ; i <= n; i++) scanf("%d", &col[i]), sum[col[i]]++, num[i].clear();
memset(head, -, sizeof(head)); tot = ;
for(int i = ; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
Add(u, v, i);
}
DFS(, -, );
for(int i = ; i < n; i++) printf("%d\n", e[i]);
}
return ;
}
CSU 1811: Tree Intersection(线段树启发式合并||map启发式合并)的更多相关文章
- 【树状数组】CSU 1811 Tree Intersection (2016湖南省第十二届大学生计算机程序设计竞赛)
题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1811 题目大意: 一棵树,N(2<=N<=105)个节点,每个节点有一种颜 ...
- CSU 1811 Tree Intersection
莫队算法,$dfs$序. 题目要求计算将每一条边删除之后分成的两棵树的颜色的交集中元素个数. 例如删除$u->v$,我们只需知道以$v$为$root$的子树中有多少种不同的颜色(记为$qq$), ...
- BZOJ_2212_[Poi2011]Tree Rotations_线段树合并
BZOJ_2212_[Poi2011]Tree Rotations_线段树合并 Description Byteasar the gardener is growing a rare tree cal ...
- poj 2892---Tunnel Warfare(线段树单点更新、区间合并)
题目链接 Description During the War of Resistance Against Japan, tunnel warfare was carried out extensiv ...
- hdu 5274 Dylans loves tree(LCA + 线段树)
Dylans loves tree Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Othe ...
- [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)
[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...
- 【BZOJ2212】[Poi2011]Tree Rotations 线段树合并
[BZOJ2212][Poi2011]Tree Rotations Description Byteasar the gardener is growing a rare tree called Ro ...
- bzoj2212/3702 [Poi2011]Tree Rotations 线段树合并
Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some in ...
- bzoj2212[Poi2011]Tree Rotations [线段树合并]
题面 bzoj ans = 两子树ans + min(左子在前逆序对数, 右子在前逆序对数) 线段树合并 #include <cstdio> #include <cstdlib> ...
随机推荐
- Kivy A to Z -- Kivy 示例演示自带名单
所有的样品已经在Android 4.04 手机正常进行 1.demo/kivycatalog 这个例子说明了如何使用主控件,例如Layout,Button,MediaPlayer,Progress B ...
- 【C#/WPF】调节图像的HSL(色相、饱和度、明亮度)
原文:[C#/WPF]调节图像的HSL(色相.饱和度.明亮度) 先说概念: HSL是一种描述颜色的方式(其他颜色描述方式还有大家熟悉的RGB值).HSL三个字母分别表示图像的Hue色相.Saturat ...
- 【C#】WixToolset快速入门教程
原文:[C#]WixToolset快速入门教程 介绍 给windows系统做软件,常见的打包工具大家可能都听说过,如:大名鼎鼎的Installshield.Inno setup等.在遇见Wix之前In ...
- TestDisk 数据恢复 重建分区表恢复文件-恢复diskpart clean
source:http://www.cgsecurity.org/wiki/TestDisk_CN TestDisk 是一款开源软件,受GNU General Public License (GPL ...
- js如何通过变量调用函数,函数名在变量里面
js如何通过变量调用函数,函数名在变量里面. 有时候函数名是动态定义的,这时候我们就需要用到这个方法了. //赋值函数名称 var a = "b"; //定义函数 function ...
- API HOOK介绍 【转】
什么是“跨进程 API Hook”? 众所周知Windows应用程序的各种系统功能是通过调用API函数来实现.API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的 ...
- Index of /android/repository
放这里了,总是记不住... https://mirrors.zzu.edu.cn/android/repository/
- duilib禁止標題欄雙擊放大窗口
創建窗口函數中使用UI_WNDSTYLE_DIALOG CMainWnd *win = new CMainWnd(_T("main_win.xml")); win->Crea ...
- delphi 获取当前进程的cpu占用率
type TProcessCpuUsage = record private FLastUsed, FLastTime: Int64; FCpuCount:Integer; publ ...
- QML Settings 小的示例
QML 中使用 Settings 可以保存一些简单的信息,例如用户名,密码,窗口位置,大小等,没有Sqlite那么麻烦,简单易用哦~~~(环境:Qt5.8 for android ,Windows ...