P1446 [HNOI2008]Cards

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1446 [HNOI2008]Cards相关的知识,希望对你有一定的参考价值。

P1446 [HNOI2008]Cards

题意:

有n张牌,染三种颜色,每种颜色规定数目,给出m种不同的洗牌方法。两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种。
求对P取模的结果

题解:

参考文章
置换群,Polya引理和burnside引理(等价类计数问题)
题目中说:输入数据保证任意多次洗牌都可用这m种洗牌法种的一种代替。这句话是burnside引理使用的理由,这句话保证了置换群的大小只会是(m+1)种(这个1指的是自己映射自己),否则置换群大小不能保证是(m+1)。
因为染色存在数量限制,所以不能用Polya定理
根据Burnside定理:等价类的个数 = 每个置换中不动元的个数和 ➗置换群的大小
现在要找不动元的个数和,那么就要把置换的每个循环节都染上相同的颜色,看有多少方案
每个置换都有若干个循环,根据所给的置换求出循环节数,考虑用dp转移来求出每个循环节染上相同的颜色,求每种颜色的总和符合题目要求的方案总数
对于每个置换,单独考虑每个循环染什么颜色,可以通过背包的方式来求。f[i][j][k]表示三种颜色分别用了i,j,k的方案,每个循环看作一个物品,物品的重量作为循环元素的个数。
答案就是不动元的个数(f[r][b][g])除以总置换数

代码:

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
    x= 0;
    char c= getchar();
    bool flag= 0;
    while (c < '0' || c > '9')
        flag|= (c == '-'), c= getchar();
    while (c >= '0' && c <= '9')
        x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
    if (flag)
        x= -x;
    read(Ar...);
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#else
    startTime = clock ();
    freopen("data.in", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#else
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn=100;
ll n,R,G,B,m;
ll mod;
ll a[maxn];
ll sz[maxn];
ll dp[maxn][maxn][maxn];
ll ans;
ll cnt;
ll vis[maxn];
ll poww(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans%mod;
}
ll solve(){
	memset(vis,0,sizeof(vis));
	cnt=0;
	for(int i=1;i<=n;i++){
		if(vis[i])continue;
		int x=i;
		int len=0;
		while(!vis[x]){
			len++;
			vis[x]=1;
			x=a[x];
		}
		sz[++cnt]=len;
	}
	memset(dp,0,sizeof(dp)),dp[0][0][0]=1;
	for(int t=1;t<=cnt;t++)	//背包 
		for(int i=R;i>=0;i--)
			for(int j=G;j>=0;j--)
				for(int k=B;k>=0;k--){
					if(i>=sz[t]) (dp[i][j][k]+=dp[i-sz[t]][j][k])%=mod;
					if(j>=sz[t]) (dp[i][j][k]+=dp[i][j-sz[t]][k])%=mod;
					if(k>=sz[t]) (dp[i][j][k]+=dp[i][j][k-sz[t]])%=mod;
				}
	return dp[R][G][B];
}
int main()
{
    //rd_test();
	read(R,G,B,m,mod);
	n=R+G+B;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			read(a[j]);
		} 
		ans=(ans+solve())%mod; 
	}
	for(int i=1;i<=n;i++)a[i]=i;
	ans=(ans+solve())%mod;
	cout<<ans*poww(m+1,mod-2)%mod<<endl;
    //Time_test();
}


以上是关于P1446 [HNOI2008]Cards的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1446 [HNOI2008]Cards

bzoj1004 HNOI2008—Cards

[HNOI2008]Cards

bzoj1004[HNOI2008]Cards

BZOJ 1004 HNOI2008 Cards

BZOJ 1004: [HNOI2008]Cards