[HNOI2008]Cards

题目描述

小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.

进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.

Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(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种洗牌法中的一种代替,且对每种

洗牌法,都存在一种洗牌法使得能回到原状态

100%数据满足 Max{Sr,Sb,Sg}<=20。

输出格式:

不同染法除以P的余数

输入输出样例

输入样例#1:  复制
1 1 1 2 7
2 3 1
3 1 2
输出样例#1:  复制
2

说明

有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。

题目大意:给出n张牌,分别染a张红色,b张蓝色,c张绿色(n=a+b+c),给出m个置换,问不同的染色方案。

红色部分就是告述你这些置换构成置换群

置换群中的染色方案问题可用polya定理解决。

只要满足同一循环染同一种色

因为3种颜色要满足数量限制,不能用polya,但可以用burnside

所以用三维背包求出在置换$a_j$下不变的元素个数$D(a_j)$

还要算上不置换,也就是单位置换

所以总共有$|G|=m+1$种置换,根据burnside引理

$ans=\frac{1}{G}\sum_{j=1}^{m+1}D(a_j)$

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int sr,sb,sg,n,m,p,inv;
  8 int vis[1001],f[101][21][21][21],tot,A[1001],s[1001],ans;
  9 int qpow(int x,int y)
 10 {
 11   int res=1;
 12   while (y)
 13     {
 14       if (y&1) res=res*x%p;
 15       x=x*x%p;
 16       y/=2;
 17     }
 18   return res;
 19 }
 20 int dfs(int x,int cnt)
 21 {
 22   if (vis[x])
 23     return cnt;
 24   vis[x]=1;
 25   return dfs(A[x],cnt+1);
 26 }
 27 int main()
 28 {int i,a,b,c;
 29   cin>>sr>>sb>>sg>>m>>p;
 30   n=sr+sb+sg;
 31   inv=qpow(m+1,p-2);
 32   while (m--)
 33     {
 34       tot=0;
 35       memset(vis,0,sizeof(vis));
 36       for (i=1;i<=n;i++)
 37     scanf("%d",&A[i]);
 38       for (i=1;i<=n;i++)
 39     if (vis[i]==0)
 40       {
 41         s[++tot]=dfs(i,0);
 42       }
 43       memset(f,0,sizeof(f));
 44       f[0][0][0][0]=1;
 45       for (i=1;i<=tot;i++)
 46     {
 47       for (a=0;a<=sr;a++)
 48         {
 49           for (b=0;b<=sb;b++)
 50         {
 51           for (c=0;c<=sg;c++)
 52             {
 53               if (a>=s[i])
 54             {
 55               f[i][a][b][c]+=f[i-1][a-s[i]][b][c];
 56               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 57             }
 58               if (b>=s[i])
 59             {
 60               f[i][a][b][c]+=f[i-1][a][b-s[i]][c],f[i][a][b][c]%=p;
 61               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 62             }
 63               if (c>=s[i])
 64             {
 65               f[i][a][b][c]+=f[i-1][a][b][c-s[i]];
 66               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 67             }
 68             }
 69         }
 70         }      
 71     }
 72       ans+=f[tot][sr][sb][sg];
 73       if (ans>=p) ans-=p;
 74     }
 75   memset(f,0,sizeof(f));f[0][0][0][0]=1;
 76   for (i=1;i<=n;i++)
 77     {
 78       for (a=0;a<=sr;a++)
 79     {
 80       for (b=0;b<=sb;b++)
 81         {
 82           for (c=0;c<=sg;c++)
 83         {
 84           if (a>=1)
 85             {
 86               f[i][a][b][c]+=f[i-1][a-1][b][c];
 87               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 88             }
 89           if (b>=1)
 90             {
 91               f[i][a][b][c]+=f[i-1][a][b-1][c],f[i][a][b][c]%=p;
 92               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 93             }
 94           if (c>=1)
 95             {
 96               f[i][a][b][c]+=f[i-1][a][b][c-1];
 97               if (f[i][a][b][c]>=p) f[i][a][b][c]-=p;
 98             }
 99         }
100         }
101     }
102     }
103   ans+=f[n][sr][sb][sg];
104   if (ans>=p) ans-=p;
105   cout<<ans*inv%p;
106 }

 

转载于:https://www.cnblogs.com/Y-E-T-I/p/7709770.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值