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启发式合并)的更多相关文章

  1. 【树状数组】CSU 1811 Tree Intersection (2016湖南省第十二届大学生计算机程序设计竞赛)

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1811 题目大意: 一棵树,N(2<=N<=105)个节点,每个节点有一种颜 ...

  2. CSU 1811 Tree Intersection

    莫队算法,$dfs$序. 题目要求计算将每一条边删除之后分成的两棵树的颜色的交集中元素个数. 例如删除$u->v$,我们只需知道以$v$为$root$的子树中有多少种不同的颜色(记为$qq$), ...

  3. BZOJ_2212_[Poi2011]Tree Rotations_线段树合并

    BZOJ_2212_[Poi2011]Tree Rotations_线段树合并 Description Byteasar the gardener is growing a rare tree cal ...

  4. poj 2892---Tunnel Warfare(线段树单点更新、区间合并)

    题目链接 Description During the War of Resistance Against Japan, tunnel warfare was carried out extensiv ...

  5. hdu 5274 Dylans loves tree(LCA + 线段树)

    Dylans loves tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  6. [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)

    [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...

  7. 【BZOJ2212】[Poi2011]Tree Rotations 线段树合并

    [BZOJ2212][Poi2011]Tree Rotations Description Byteasar the gardener is growing a rare tree called Ro ...

  8. bzoj2212/3702 [Poi2011]Tree Rotations 线段树合并

    Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some in ...

  9. bzoj2212[Poi2011]Tree Rotations [线段树合并]

    题面 bzoj ans = 两子树ans + min(左子在前逆序对数, 右子在前逆序对数) 线段树合并 #include <cstdio> #include <cstdlib> ...

随机推荐

  1. log4net使用记录

    1.在程序中引用log4net.dll 2.添加-新建配置文件Log4Net.config,并在文件属性中“复制到输出目录”选中“始终复制”,文件内容如下: <?xml version=&quo ...

  2. abp框架(aspnetboilerplate)设置前端报错显示

    abp在后端抛出异常 throw new UserFriendlyException($"抛出一个错误"); 在发布之前,需要设置是否把报错发送给前端 如果将此设置为true,则会 ...

  3. 通通玩blend美工(6)上——仿iPhone滚动选择器的ListBox(UI设计)

    原文:通通玩blend美工(6)上--仿iPhone滚动选择器的ListBox(UI设计) 好久没更新博客了,由于项目比较紧,期间收到不少园友的短消息,感谢大家对我的支持~~. 相信各位都在自己的神机 ...

  4. JavaScript生成树形菜单(递归算法)

    1.最终效果图(这里仅为实现算法,并加载至页面,不做任何css界面优化) 注释:本示例包含三级目录菜单,但实际上可支持N级(可使用该代码自行测试) 2.数据源 菜单信息一般来源于数据库中数据表,且为自 ...

  5. 正试图在 os 加载程序锁内执行托管代码

    正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain 或映像初始化函数内运行托管代码... 当我在窗体初始化的时候,调用了一个外部的dill时,它就不知什么原因的 抛出一个“正试图在 ...

  6. Cannot read property 'substring' of undefined

    这个是在使用 jquery的ztree插件过程中遇到的错误 原因是数据的格式和规定的格式不一致,需要把数据按照模板给的样子来

  7. ToolBarTray与ToolBarPanel的区别

    ToolBarTray 类:表示处理ToolBar 的布局的容器.用于ToolBar控件模板之中. e.g. <ToolBarTray> <ToolBar> <Butto ...

  8. 从IntToHex()说起,栈/堆地址标准写法 good

    学习中的一些牢骚.栈/堆地址标准写法. 2017-02-12 • 杂谈 • 暂无评论 • 老衲 •浏览 226 次 我一直都在寻找各种业务功能的最简单写法,用减法的模式来开发软件.下面是我的写法,如果 ...

  9. string与QString转换(string既可以是utf8,也可以是gbk)

    AtUtf8.h #ifndef _QT_UTF8_H #define _QT_UTF8_H #include <QString> #include <string> usin ...

  10. 发现 TSplitter 在嵌套时不好用, 索性写了个替代品(处理MouseDown,MouseMove,MouseUp,然后设定控件的Left值就可以了)

    代替 TSplitter 的 TDirPanel 类: unit DirPanel; interface uses   Classes, Controls, Forms, ExtCtrls; type ...