HDU 5977 Garden of Eden【树分治】

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5977

题意:

给定$n$个结点的树,每个结点有一种颜色,一共有$k$种颜色,每个结点只能访问一次,问最后有多少种不同的路径可以访问完所有颜色。
数据范围:$1 \le n \le 50000, 1 \le k \le 10$

分析:

$k$只有$10$,显然要状压一下。在某棵子树中,枚举该子树能到达的所有可能的颜色情况,去找有多少其他情况与其或后值为$(1<<k)-1$,这里使用高维前缀和,将每个集合出现个数加上其超集出现个数。
然后就是树分治的套路了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
typedef pair<int, int>p;
typedef long long ll;
const int maxn = 5e4 + 5, oo = 0x3f3f3f3f;
int tot;
int n, k;
struct EDGE{
int to;
int nxt;
}edge[maxn << 1];
int kind[maxn];
int head[maxn];
bool used[maxn];
int sub_sz[maxn];
ll ans;
ll cnt[1305];
int a[maxn];
int mask;
int ROOT;
int ANS;
void init(int n)
{
kind[0] = 0;
mask = (1 << k) - 1;
memset(head, -1, sizeof(head));
ans = 0;
tot = 0;
}
int dfs_size(int u, int pa)
{
int sz = 1;
for(int i = head[u]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(to == pa || used[to]) continue;
sz += dfs_size(to, u);
}
sub_sz[u] = sz;
return sz;
}
void dfs_root(int u, int pa, int sz)
{
int m = 0;
int t = 0;
for(int i = head[u]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(to == pa || used[to]) continue;
dfs_root(to, u, sz);
m = max(m, sub_sz[to]);
t += m;
}
m = max(m, sz - t);
if(ANS > m){
ANS = m;
ROOT = u;
}
}
void cal_kind(int u, int p, int d)
{
kind[++kind[0]] = d;
for(int i = head[u]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(to == p || used[to]) continue;
cal_kind(to, u, d | (1 << a[to]));
}
}
ll count_kind(int u, int d)
{
ll res = 0;
kind[0] = 0;
cal_kind(u, u, d);
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= kind[0]; ++i) cnt[kind[i]]++;
for(int i = 0; i < k; ++i){
for(int j = mask; j >= 1; --j){
if((j >> i) & 1) cnt[j ^ (1 << i)] += cnt[j];
}
}
for(int i = 1; i <= kind[0]; ++i){
res += cnt[mask ^ kind[i]];
}
return res;
}
void solve(int u)
{
dfs_size(u, u);
ANS = n;
dfs_root(u, u, sub_sz[u]);
int s = ROOT;
used[s] = true;
ans += count_kind(s, 1 << a[s]);
for(int i = head[s]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(used[to]) continue;
solve(to);
}
for(int i = head[s]; i != -1; i = edge[i].nxt){
int to = edge[i].to;
if(used[to]) continue;
ans -= count_kind(to, (1 << a[s]) | (1 << a[to]));
}
used[s] = false;
}
int main (void)
{
while(~scanf("%d%d", &n, &k)){
init(n);
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
a[i]--;
}
int u, v;
for(int i = 0; i < n - 1; ++i){
scanf("%d%d", &u, &v);
edge[tot].to = u;
edge[tot].nxt = head[v];
head[v] = tot++;
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
solve(1);
printf("%lld\n", ans);
}
return 0;
}
文章目录
  1. 1. 题目链接:
  2. 2. 题意:
  3. 3. 分析:
  4. 4. 代码: