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
", ans);
60 }
61 
62 int main()
63 {
64     init();
65     solve();
66     
67     return 0;
68 }
原文地址:https://www.cnblogs.com/aseer/p/8441024.html