呜呼。。NOI前一个月正式开始切BZOJ了……以后的题解可能不会像之前的零散风格了,一套题我会集中起来发,遇到一些需要展开总结的东西我会另开文章详细介绍。
用了一天的时间把HNOI2008这套题切了……感觉新知识好多啊……一定是我太弱了,各方面能力还都需要加强,尤其是DP啦推导啦神马的
BZOJ1004 Cards:
题目大意:
桌上有N张牌,将这N张牌染成sr张红色,sg张绿色和sb张蓝色,然后给出M种洗牌方式,可以通过洗牌得到的方案视作相同方案,问不同的染色方案数。输入中保证任何一种连续多次洗牌的方式都可以用这M种洗牌中的一种替换
题目分析:
很显然给出的M种洗牌实际上是M种不同的置换,然后这是一个裸的置换群问题。但是我不会啊,然后昨晚就查呀查,总算搞明白了:可以用Burnside定理解决这个问题。
置换和置换群是一套成型的理论,在自己没太搞明白的时候在这里展开介绍纯属作死……所以附上传送门:
http://wenku.baidu.com/view/9386b9d9d15abe23482f4d02.html
首先这道题输入非常人性,它保证给出的置换加上单位置换之后就是一个置换群了。但是是不可以直接用Ploya做的,因为它限定了每种颜色的数量,但是我们可以借助这个思想:群中的每个置换的不动点个数等价于给每个循环节染成相同颜色的方案数,所以只需要在外面套一个DP就可以了。
知识点:群论、DP、数学
1 #include <cstdio> 2 #include <cstring> 3 4 const int maxp = 105; 5 6 int sr, sb, sg, n, m, p, ans; 7 int inv[maxp], num[maxp], had[maxp], tp[maxp], dp[maxp][30][30][30]; 8 9 void dfs(int v, int &len){had[v] = 1; if(!had[num[v]]) dfs(num[v], ++len);} 10 inline int pow(int n, int k) 11 { 12 int ans = 1; 13 while(k) 14 { 15 if(k & 1) ans = ans * n % p; 16 n = n * n % p; k >>= 1; 17 } 18 return ans; 19 } 20 21 void work(int k, int lr, int lg, int lb) 22 { 23 int ans = 0; 24 if(lr >= tp[k]) 25 { 26 if(dp[k - 1][lr - tp[k]][lg][lb] == -1) work(k - 1, lr - tp[k], lg, lb); 27 ans += dp[k - 1][lr - tp[k]][lg][lb]; 28 } 29 if(lg >= tp[k]) 30 { 31 if(dp[k - 1][lr][lg - tp[k]][lb] == -1) work(k - 1, lr, lg - tp[k], lb); 32 ans += dp[k - 1][lr][lg - tp[k]][lb]; 33 } 34 if(lb >= tp[k]) 35 { 36 if(dp[k - 1][lr][lg][lb - tp[k]] == -1) work(k - 1, lr, lg, lb - tp[k]); 37 ans += dp[k - 1][lr][lg][lb - tp[k]]; 38 } 39 dp[k][lr][lg][lb] = ans % p; 40 } 41 42 int main() 43 { 44 scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &p); 45 46 n = sr + sb + sg; 47 48 inv[1] = 1; 49 for(int i = 2; i < p; ++i) inv[i] = inv[p % i] * (p - p / i) % p; 50 51 for(int j = 1; j <= n; ++j) num[j] = j; 52 for(int i = 1; i <= n; ++i) tp[i] = 1; 53 memset(dp, 0xFF, sizeof dp); 54 dp[0][0][0][0] = 1; 55 work(n, sr, sg, sb); 56 ans = dp[n][sr][sg][sb] % p; 57 58 for(int i = 1; i <= m; ++i) 59 { 60 for(int j = 1; j <= n; ++j) scanf("%d", &num[j]); 61 memset(had, 0, sizeof had); 62 int len = 0, tk; 63 for(int j = 1; j <= n; ++j) if(!had[j]){dfs(j, tk = 1); tp[++len] = tk;} 64 memset(dp, 0xFF, sizeof dp); 65 dp[0][0][0][0] = 1; 66 work(len, sr, sg, sb); 67 ans = (ans + dp[len][sr][sg][sb]) % p; 68 } 69 70 ans = ans * inv[m + 1] % p; 71 printf("%d\n", ans); 72 return 0; 73 }