Aiiage Camp Day1 C Littrain wanna be different
题意
给一棵N个点的树,每个点有一个颜色。问含有至少k种颜色的最小连通块的大小。
1<=n<=1e4, 1<=k<=5,1<=颜色数<=n
题解
如果只有k种颜色,有个显然的O(3^k*n)的DP。
DP[i][j]表示以i为根的树,颜色状态为j的最小连通块大小。j用k位二进制数表示。DFS转移,每次枚举所有状态即可。
那么将所有颜色映射到[1,k],做这样的DP,正确概率为k!/k^k。多随机几次即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n, k, tot(0), e[20010], G[10010], nxt[20010], c[10010], col[10010], dp[10010][50]; 5 6 void addedge(int u, int v) 7 { 8 e[++tot] = u; nxt[tot] = G[v]; G[v] = tot; 9 e[++tot] = v; nxt[tot] = G[u]; G[u]=tot; 10 } 11 12 inline void init() 13 { 14 srand(0); 15 scanf("%d%d", &n, &k); 16 for (int i = 1; i <= n; ++i) 17 scanf("%d", c + i); 18 for (int i = 1; i < n; ++i) 19 { 20 int x, y; 21 scanf("%d%d", &x, &y); 22 addedge(x, y); 23 } 24 } 25 26 inline void DFS(int now, int fa) 27 { 28 dp[now][col[c[now]]] = 1; 29 for (int i = G[now]; i; i = nxt[i]) 30 if (e[i] != fa) 31 { 32 DFS(e[i], now); 33 for (int j = 0; j < (1 << k); ++j) 34 for (int k = j; k; k = (k - 1) & j) 35 dp[now][j] = min(dp[now][j], dp[now][k ^ j] + dp[e[i]][k]); 36 } 37 for (int j = 0; j < (1 << k); ++j) 38 for (int k = j; k; k = (k - 1) & j) 39 dp[now][k] = min(dp[now][k], dp[now][j]); 40 dp[now][0] = 0; 41 } 42 43 inline int Work() 44 { 45 memset(dp, 1, sizeof dp); 46 DFS(1, 0); 47 return dp[1][(1 << k) - 1]; 48 } 49 50 inline void solve() 51 { 52 int ans(1000000000); 53 for (int ii = 0; ii < 50; ++ii) 54 { 55 for (int i = 1; i <= n; ++i) 56 col[i] = 1 << rand() % k; 57 ans = min(ans, Work()); 58 } 59 printf("%d\n", ans); 60 } 61 62 int main() 63 { 64 init(); 65 solve(); 66 67 return 0; 68 }