P1446 [HNOI2008]Cards
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的主要内容,如果未能解决你的问题,请参考以下文章