zoukankan      html  css  js  c++  java
  • 【bzoj1004】[HNOI2008]Cards Burnside引理+背包dp

    题目描述

    用三种颜色染一个长度为 $n=Sr+Sb+Sg$ 序列,要求三种颜色分别有 $Sr,Sb,Sg$ 个。给出 $m$ 个置换,保证这 $m$ 个置换和置换 ${1,2,3,...,nchoose 1,2,3,...,n}$ 构成一个置换群,求置换后不同构的序列个数模 $p$ 。

    $0le Sr,Sb,Sgle 20,0le mle 60,m+1le ple 100$ ,$p$ 是质数。

    输入

    第一行输入 5 个整数:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。
    接下来 m 行,每行描述一种洗牌法,每行有 n 个用空格隔开的整数 X1X2...Xn ,恰为 1 到 n 的一个排列,表示使用这种洗牌法,第 i 位变为原来的 Xi 位的牌。输入数据保证任意多次洗牌都可用这 m 种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

    输出

    不同染法除以P的余数

    样例输入

    1 1 1 2 7
    2 3 1
    3 1 2

    样例输出

    2


    题解

    Burnside引理+背包dp

    由于颜色有3种,因此不能直接使用Polya定理。

    考虑Burnside引理推导Polya定理的过程:对于一种置换,不动点需要满足:每个循环种的颜色相同。

    这种推导即可应用于本题。我们对于一个置换,取出其所有循环,这个循环需要 循环大小 个同种颜色。

    显然是一个背包dp。设 $f[i][j][k]$ 表示前 $i$ 个置换,用了 $j$ 种颜色1和 $k$ 种颜色2的方案数(用了 $sum_i-j-k$ 种颜色3)。那么对于第 $i$ 个置换,讨论其颜色即可转移。

    最终对于该置换的不动点数目即为 $f[k][Sr][Sb]$ ,$k$ 为循环数目。

    把所有置换(包括置换后得到本身的置换 ${1,2,3,...,nchoose 1,2,3,...,n}$ )的不动点数目加起来,乘以 $m$ 的逆元即为答案。

    时间复杂度 $O(mn^3)$

    #include <cstdio>
    #include <cstring>
    int a , b , c , p , f[65][25][25] , v[65] , vis[65];
    int solve()
    {
    	int tot = 0 , sum = 0 , w , i , j , k;
    	memset(vis , 0 , sizeof(vis));
    	memset(f , 0 , sizeof(f));
    	f[0][0][0] = 1;
    	for(i = 1 ; i <= a + b + c ; i ++ )
    	{
    		if(!vis[i])
    		{
    			tot ++ ;
    			for(w = 0 , j = i ; !vis[j] ; j = v[j])
    				vis[j] = 1 , w ++ ;
    			sum += w;
    			for(j = 0 ; j <= a ; j ++ )
    			{
    				for(k = 0 ; k <= b ; k ++ )
    				{
    					if(sum - j - k <= c)
    					{
    						if(j >= w) f[tot][j][k] += f[tot - 1][j - w][k];
    						if(k >= w) f[tot][j][k] += f[tot - 1][j][k - w];
    						if(sum - j - k >= w) f[tot][j][k] += f[tot - 1][j][k];
    						f[tot][j][k] %= p;
    					}
    				}
    			}
    		}
    	}
    	return f[tot][a][b];
    }
    int main()
    {
    	int m , i , j , ans = 0;
    	scanf("%d%d%d%d%d" , &a , &b , &c , &m , &p);
    	for(i = 1 ; i <= a + b + c ; i ++ ) v[i] = i;
    	ans = solve();
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		for(j = 1 ; j <= a + b + c ; j ++ ) scanf("%d" , &v[j]);
    		ans = (ans + solve()) % p;
    	}
    	for(i = 1 ; i <= p - 2 ; i ++ ) ans = ans * (m + 1) % p;
    	printf("%d
    " , ans);
    	return 0;
    }
    

    我才不会告诉你们下面的代码也能过呢(数据太水了 = =)

    #include <cstdio>
    int p;
    int pow(int x , int y)
    {
    	int ans = 1;
    	while(y)
    	{
    		if(y & 1) ans = ans * x % p;
    		x = x * x % p , y >>= 1;
    	}
    	return ans;
    }
    int main()
    {
    	int a , b , c , m , i , ans = 1;
    	scanf("%d%d%d%d%d" , &a , &b , &c , &m , &p);
    	for(i = 1 ; i <= a + b + c ; i ++ ) ans = ans * i % p;
    	for(i = 1 ; i <= a ; i ++ ) ans = ans * pow(i , p - 2) % p;
    	for(i = 1 ; i <= b ; i ++ ) ans = ans * pow(i , p - 2) % p;
    	for(i = 1 ; i <= c ; i ++ ) ans = ans * pow(i , p - 2) % p;
    	ans = ans * pow(m + 1 , p - 2) % p;
    	printf("%d
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    网络基础、多线程、ftp任务铺垫
    文件上传下载、socketserver(并发)、解读socketserver源码
    模拟ssh、黏包、hashlib模块(MD5)
    面向对象多继承(C3算法)/网络编程
    Slideout吐槽
    HDU 1756 Cupid's Arrow 判断点在多边形的内部
    POJ 1584 A Round Peg in a Ground Hole 判断凸多边形,判断点在凸多边形内
    位运算 找出给定的数中其他数都是两个,有两个是一个的数
    NYOJ 1107 最高的奖励(贪心+优先队列)
    POJ 2653 Pick-up sticks (判断线段相交)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8297490.html
Copyright © 2011-2022 走看看