题目描述
传送门-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
1≤T≤1000,1≤n≤60,1≤p<230,0≤ci≤60
思路
看到这道题直接脑袋爆炸…
好的,这种去重计数一般都要想到Burnside和Polya
我们考虑Polya定理:
L
=
1
∣
G
∣
∗
∑
i
=
0
∣
G
∣
m
f
i
L=\frac{1}{|G|}* \sum_{i=0}^{|G|}m^{f_i}
L=∣G∣1∗i=0∑∣G∣mfi
其中
L
,
∣
G
∣
,
m
,
f
i
L,|G|,m,f_i
L,∣G∣,m,fi分别表示方案数, 置换个数(有几种置换), 可选颜色数和第
i
i
i 种方案中循环个数
然后我们可以发现,这个神奇的几何体就是正12面体把每一个面中间那个压下去形成的图形…可以借助这个图形思考思考:
然后我们考虑旋转方案:
-
不动
显然每个面都是相对独立的,每个循环长度就是1,一共有 60 / 1 = 60 60/1=60 60/1=60个循环 -
棱
脑补啊…相对的两个面就是可以旋转的,就转180°就可以了,数一数正十二面体一共有30条棱,所以置换数就是 30 / 2 ∗ 1 = 15 30/2*1=15 30/2∗1=15 每个循环长度为2,每个置换中一共有 60 / 2 = 30 60/2=30 60/2=30 个循环 -
点
也就是三个面之间可通过旋转得到,所以循环长度就是3,循环个数就是 60 / 3 = 20 60/3=20 60/3=20 个,一共有20个顶点,旋转120°,240°,所以置换个数就是 20 / 2 ∗ 2 = 20 20/2*2=20 20/2∗2=20 个 -
面
我们将正十二面体相对两个面的几何中心连线作为旋转轴,然后可以发现转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 6∗4=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]+15∗f[2]+20∗f[3]+24∗f[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=∣G∣1∗i=0∑∣G∣mfi
其中
L
,
∣
G
∣
,
m
,
f
i
L,|G|,m,f_i
L,∣G∣,m,fi分别表示方案数, 置换个数(有几种置换), 可选颜色数和第
i
i
i 种方案中循环个数
我们知道这里的
∣
G
∣
|G|
∣G∣ 大小为 60,但是本质上只有4种置换、
这里的循环个数对应为
60
,
30
,
20
,
12
60,30,20,12
60,30,20,12
这里的循环长度对应为
1
,
2
,
3
,
5
1,2,3,5
1,2,3,5 (不动,棱,点,面),我们记为
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]个
m∗m∗m∗...∗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]:前i种颜色染j个循环的方案数
这里的方案数是排列数
所以有状态转移方程:
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[i−1][j−k]∗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=60∗p
最后将答案除以60即可,原理如下:
∵
x
≡
q
(
m
o
d
60
∗
p
)
∵ x≡q(mod\quad 60*p)
∵x≡q(mod60∗p)
∴
x
=
60
∗
p
∗
k
+
q
∴ x=60*p*k+q
∴x=60∗p∗k+q
∵
60
∣
x
,
60
∣
=
60
∗
p
∗
k
x
,
p
,
k
,
q
∈
N
∵60|x,60|=60*p*k\quad x,p,k,q∈N
∵60∣x,60∣=60∗p∗kx,p,k,q∈N
∴
q
∣
60
∴ q|60
∴q∣60
设
q
=
60
∗
t
设q=60*t
设q=60∗t
∴
x
60
≡
t
(
m
o
d
p
)
∴ \frac{x}{60}≡t(mod\quad p)
∴60x≡t(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;
}