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. python解决urllib2乱码问题

    示例: #!/usr/bin/env python # -*- coding: utf-8 -*- import urllib import urllib2 def main(): url = &qu ...

  2. 浅谈 Swift 中的 Optionals

    input[type="date"].form-control,.input-group-sm>input[type="date"].input-grou ...

  3. Matlab随笔之三维图形绘制

    1.三维曲线 用到了plot3函数.plot3(x,y,z)用来绘制3维曲线图,而不能绘制曲面图!就是把所有的(x,y,z)点连接在一起. t=linspace(,*pi,); x=sin(t); y ...

  4. WPF DataGrid 触发器

    <DataGrid.RowHeaderStyle> <Style TargetType="DataGridRowHeader"> <Style.Tri ...

  5. ubuntu 关闭 phpmyadmin

    apache 有很多管理服务器的命令.apt-get installl phpmyadmin后突然想暂时关闭phpmyadmin,只需要a2disconf phpmyadmin即可.需要启用的时候再输 ...

  6. C#调用C/C++ DLL 参数传递和回调函数的总结

    原文:C#调用C/C++ DLL 参数传递和回调函数的总结 Int型传入: Dll端: extern "C" __declspec(dllexport) int Add(int a ...

  7. C# 特性的使用

    using ClassLibrary;using System;using System.Collections.Generic;using System.Linq;using System.Refl ...

  8. ORM 集合

    1.EF   https://github.com/aspnet 2.Chloe.ORM http://www.cnblogs.com/so9527/p/5809089.html http://www ...

  9. WPF属性(二)附加属性

    原文:WPF属性(二)附加属性 附加属性是说一个属性本来不属于某个对象,但由于某种需求而被后来附加上,也就是把对象放入一个特定环境后对象才具有的属性就称为附加属性,附加属性的作用就是将属性与数据类型解 ...

  10. 关于DDD领域驱动设计的理论知识收集汇总

    原文:关于DDD领域驱动设计的理论知识收集汇总 最近一直在学习领域驱动设计(DDD)的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下: 我一直觉得不要盲目相信权威,比如不能一 ...