Kaleidoscope(HDU-6360)(Polya定理)

4 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

传送门-Vjudge
传送门-HDU
题目大意:给你一个菱形6面体(共60面),然后给你 n n n 种颜色给它每一个面上色,要求第 i i i 种颜色必须至少涂 c [ i ] c[i] c[i] 次,问你本质不同的方案数,旋转相同视作同一种方案,染色方案数对 p p p 取模,多组数据
数据范围: 1 ≤ T ≤ 1000 , 1 ≤ n ≤ 60 , 1 ≤ p < 2 30 , 0 ≤ c i ≤ 60 1\le T\le 1000,1\le n\le 60,1\le p< 2^{30},0\le c_i\le60 1T1000,1n60,1p<230,0ci60
在这里插入图片描述

思路

看到这道题直接脑袋爆炸…
好的,这种去重计数一般都要想到Burnside和Polya
我们考虑Polya定理:

L = 1 ∣ G ∣ ∗ ∑ i = 0 ∣ G ∣ m f i L=\frac{1}{|G|}* \sum_{i=0}^{|G|}m^{f_i} L=G1i=0Gmfi
其中 L , ∣ G ∣ , m , f i L,|G|,m,f_i LGmfi分别表示方案数, 置换个数(有几种置换), 可选颜色数和第 i i i 种方案中循环个数

然后我们可以发现,这个神奇的几何体就是正12面体把每一个面中间那个压下去形成的图形…可以借助这个图形思考思考:
然后我们考虑旋转方案:

  1. 不动
    显然每个面都是相对独立的,每个循环长度就是1,一共有 60 / 1 = 60 60/1=60 60/1=60个循环


  2. 脑补啊…相对的两个面就是可以旋转的,就转180°就可以了,数一数正十二面体一共有30条棱,所以置换数就是 30 / 2 ∗ 1 = 15 30/2*1=15 30/21=15 每个循环长度为2,每个置换中一共有 60 / 2 = 30 60/2=30 60/2=30 个循环


  3. 也就是三个面之间可通过旋转得到,所以循环长度就是3,循环个数就是 60 / 3 = 20 60/3=20 60/3=20 个,一共有20个顶点,旋转120°,240°,所以置换个数就是 20 / 2 ∗ 2 = 20 20/2*2=20 20/22=20


  4. 我们将正十二面体相对两个面的几何中心连线作为旋转轴,然后可以发现转72°,144°,216°,288°会与原图形重合。
    我们将正十二面体相对两个面的几何中心连线作为旋转轴,然后可以发现转72°,144°,216°,288°会与原图形重合,
    所以面的循环长度就是5,(难以解释…),脑补一下,每个下凹的面中5个面就算一个循环,一共就有5个循环,而这种对称轴一共有 12 / 2 = 6 12/2=6 12/2=6 个,也就是一共有 6 ∗ 4 = 24 6*4=24 64=24 种置换

假设我们记 f [ i ] f[i] f[i] 为上面第 i i i 种旋转方案的一种置换中的本质不同染色方案数,那么答案就为:

A n s = f [ 1 ] + 15 ∗ f [ 2 ] + 20 ∗ f [ 3 ] + 24 ∗ f [ 4 ] 60 Ans=\frac{f[1]+15*f[2]+20*f[3]+24*f[4]}{60} Ans=60f[1]+15f[2]+20f[3]+24f[4]
好了我们再来看看 P o l y a Polya Polya 定理:
L = 1 ∣ G ∣ ∗ ∑ i = 0 ∣ G ∣ m f i L=\frac{1}{|G|}* \sum_{i=0}^{|G|}m^{f_i} L=G1i=0Gmfi
其中 L , ∣ G ∣ , m , f i L,|G|,m,f_i LGmfi分别表示方案数, 置换个数(有几种置换), 可选颜色数和第 i i i 种方案中循环个数
我们知道这里的 ∣ G ∣ |G| G 大小为 60,但是本质上只有4种置换、
这里的循环个数对应为 60 , 30 , 20 , 12 60,30,20,12 60302012
这里的循环长度对应为 1 , 2 , 3 , 5 1,2,3,5 1235 (不动,棱,点,面),我们记为 s i z [ i ] siz[i] siz[i]
这里颜色加了限制,第 i i i 种颜色必须大于 c [ i ] c[i] c[i] ,似乎很难搞。。。
我们再来看看 P o l y a Polya Polya 定理原理,就是在每个置换中,将同一循环中的元素(面)染成同一种颜色
也就是说,
P o l y a Polya Polya 定理单个置换中 m f i m^{f_i} mfi 可以拆成 m ∗ m ∗ m ∗ . . . ∗ m ⏟ f [ i ] 个 \underbrace{m*m*m*...*m}_{f[i]个} f[i] mmm...m表示是由每个循环选择的乘积。
这是所有循环面对颜色选择相同且无限制的情况下
然后在这里颜色限制也就是
t o t = ⌈ c [ i ] s i z [ j ] ⌉ tot=\lceil \frac{c[i]}{siz[j]}\rceil tot=siz[j]c[i]必须染这么多个循环,然后我们就可以考虑 d p dp dp
我们定义 f [ i ] [ j ] : 前 i 种 颜 色 染 j 个 循 环 的 方 案 数 f[i][j]:前i种颜色染j个循环的方案数 f[i][j]ij
这里的方案数是排列数
所以有状态转移方程:
f [ i ] [ j ] = s u m { f [ i − 1 ] [ j − k ] ∗ C [ j ] [ k ] } f[i][j]=sum\{f[i-1][j-k]*C[j][k]\} f[i][j]=sum{f[i1][jk]C[j][k]}
那么答案就是 f [ n ] [ 60 / s i z [ t ] ] f[n][60/siz[t]] f[n][60/siz[t]]
我们要注意一个细节,由于 p p p 是任意的,所以我们可以将 M o d = 60 ∗ p Mod=60*p Mod=60p
最后将答案除以60即可,原理如下:
∵ x ≡ q ( m o d 60 ∗ p ) ∵ x≡q(mod\quad 60*p) xq(mod60p)
∴ x = 60 ∗ p ∗ k + q ∴ x=60*p*k+q x=60pk+q
∵ 60 ∣ x , 60 ∣ = 60 ∗ p ∗ k x , p , k , q ∈ N ∵60|x,60|=60*p*k\quad x,p,k,q∈N 60x,60=60pkx,p,k,qN
∴ q ∣ 60 ∴ q|60 q60
设 q = 60 ∗ t 设q=60*t q=60t
∴ x 60 ≡ t ( m o d p ) ∴ \frac{x}{60}≡t(mod\quad p) 60xt(modp)

即答案直接除以60,最后一步为模运算除法

代码

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
LL read(){
    LL f=1,x=0;char c=getchar();
    while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
    while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return f*x;
}
#define MAXN 60
#define INF 0x3f3f3f3f
LL Mod;
inline LL Add(LL x,LL y){
	x+=y;
	if(x>=Mod) x-=Mod;
	return x;
}
LL Mul(LL x,LL y){
	LL ret=0;
	while(y){
		if(y&1) ret=Add(ret,x);
		x=Add(x,x);
		y>>=1;
	}
	return ret;
}
LL f[MAXN+5],C[MAXN+5][MAXN+5];
int c[MAXN+5],siz[4]={1,2,3,5},cnt[4]={1,15,20,24},tot[4];
int main(){//siz[i]:第i种置换循环长
	int T=read();
	while(T--){
		int n=read();
		memset(tot,0,sizeof(tot));
		LL p=read();
		Mod=60*p;
		for(int i=0;i<=MAXN;i++)
			C[i][0]=C[i][i]=1;
		for(int i=1;i<=MAXN;i++)
			for(int j=1;j<i;j++)
				C[i][j]=Add(C[i-1][j],C[i-1][j-1]);
		for(int i=0;i<n;i++){
			c[i]=read();
			for(int j=0;j<4;j++)
				tot[j]+=(c[i]+siz[j]-1)/siz[j];
		}
		LL ans=0;
		for(int t=0;t<4;t++){
			int m=60/siz[t];
			if(tot[t]>m) continue;
			memset(f,0,sizeof(f));f[0]=1;
			for(int i=0;i<n;i++){
				int tmp=(c[i]+siz[t]-1)/siz[t];
				for(int j=m;j>=0;j--){
					LL tp=0;
					for(int k=tmp;k<=j;k++)
						tp=Add(tp,Mul(f[j-k],C[j][k]));
					f[j]=tp;
				}
			}
			ans=Add(ans,Mul(f[m],cnt[t]));
		}
		printf("%lld\n",ans/60);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值