gym102114K. Kaleidoscope
神必burnside题
题目大意
给出一个60面体,求用n种颜色染色的方案数(旋转同构),第i种要用至少\(c_i\)次
对p取模(p不是质数)
展开图:
题解
显然一眼burnside/polya,考虑求出所有的置换
感受一下,一个二维的正方形需要1种顺时针旋转90°得到所有置换,一个三维的正方体需要2种90°旋转得到所有置换,因此三维的60面体只用两种操作的组合就可以得到所有置换
操作1选用面{9,3,27,21,15}顺时针旋转一格,操作2选用面{1,2,30,29,28,2}顺时针旋转一格,具体怎么动可以以初始面为中心,往外按旋转方向一圈圈找(画图感受一下,有个特性就是每圈经过一个五边形小方格时包含的形状都是一样的)
操作1就是先顺时针再逆时针(展开图),操作2如下:
红圈顺时针,蓝圈逆时针
然后用两种操作排列组合,可以得到共60种不同的置换,并且都是每个置换中的每个环大小相同(1,2,3,5四种)
直接对这4中置换分别dp每种颜色多少个就行了,因为p不是质数,所以要将数字维护成60a+b的形式
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (int a=b; a<=c; a++)
#define fd(a,b,c) for (int a=b; a>=c; a--)
#define mod1 998244353
#define mod2 1000000007
#define add(a,b) a=((a)+(b))%mod
#define ll long long
#define file
using namespace std;
const int N=61;
struct rep{
int p[N];
void init()
{
fo(i,1,60) p[i]=i;
}
} r1,r2;
struct n60{
ll x,y; //60x+y
};
int T,n,mod,tot;
int c[N];
vector<rep> rset;
map<ll,bool> rset_bz;
vector<int> rset_clen[61];
ll clen_sum[6];
n60 C[61][61];
n60 f[N][61];
n60 ans;
rep operator+ (const rep &a, const rep &b)
{
static rep c;
c.init();
fo(i,1,60) c.p[i]=b.p[a.p[i]];
return c;
}
n60 operator+ (const n60 &a, const n60 &b)
{
static n60 c;
c.y=a.y+b.y;
c.x=(a.x+b.x+c.y/60)%mod;
c.y%=60;
return c;
}
n60 operator* (const n60 &a, const n60 &b)
{
static n60 c;
c.y=a.y*b.y;
c.x=(a.x*b.x%mod*60+a.x*b.y+a.y*b.x+c.y/60)%mod;
c.y%=60;
return c;
// (60ax+ay)(60bx+by)=60(60axbx+axby+aybx)+ayby
}
n60 operator* (const n60 &a, const ll &b)
{
static n60 c;
c.y=a.y*b;
c.x=(a.x*b+c.y/60)%mod;
c.y%=60;
return c;
}
ll qpower(ll a,int b)
{
ll ans=1;
while (b)
{
if (b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void round(rep &r,vector<int> v)
{
int len=v.size();
fo(i,0,len-1)
{
if (r.p[v[i]]!=v[i])
cout<<"error"<<endl;
r.p[v[i]]=v[(i+len/5)%len];
}
}
void check(rep &r)
{
fo(i,1,60) if (r.p[i]==i) cout<<i<<" error"<<endl;
}
ll rep_hash(const rep &r)
{
ll hs1=0,hs2=0;
fo(i,1,60)
{
hs1=(hs1*233+r.p[i])%mod1;
hs2=(hs2*345+r.p[i])%mod2;
}
return hs1*mod2+hs2;
}
void init()
{
rep r0,r_op;
r1.init();
round(r1,{9,3,27,21,15});
round(r1,{8,4,2,28,26,22,20,16,14,10});
round(r1,{7,6,5,1,30,29,25,24,23,19,18,17,13,12,11});
round(r1,{53,54,55,59,60,31,35,36,37,41,42,43,47,48,49});
round(r1,{52,56,58,32,34,38,40,44,46,50});
round(r1,{51,57,33,39,45});
check(r1);
r2.init();
round(r2,{1,30,29,28,2});
round(r2,{60,31,35,36,25,26,27,3,4,5});
round(r2,{59,58,32,34,38,37,24,23,22,21,15,9,8,7,6});
round(r2,{54,55,56,57,33,39,40,41,42,19,20,16,14,10,11});
round(r2,{53,52,51,45,44,43,18,17,13,12});
round(r2,{49,50,46,47,48});
check(r2);
r0.init();
rset.push_back(r0);
rset_bz[rep_hash(r0)]=1;
tot=0;
while (tot<rset.size())
{
ll hs;
r_op=rset[tot]+r1;
hs=rep_hash(r_op);
if (rset_bz.find(hs)==rset_bz.end()) rset_bz[hs]=1,rset.push_back(r_op);
r_op=rset[tot]+r2;
hs=rep_hash(r_op);
if (rset_bz.find(hs)==rset_bz.end()) rset_bz[hs]=1,rset.push_back(r_op);
++tot;
}
bool bz[N];
fo(i,0,tot-1)
{
memset(bz,0,sizeof(bz));
rset_clen[i].clear();
fo(j,1,60)
if (!bz[j])
{
int len=0,k=j;
while (!bz[k]) bz[k]=1,k=rset[i].p[k],++len;
rset_clen[i].push_back(len);
}
// for (auto s:rset_clen[i]) cout<<s<<" ";cout<<endl;
++clen_sum[rset_clen[i][0]];
}
}
int main()
{
// freopen("K.in","r",stdin);
init();
scanf("%d",&T);
for (;T;--T)
{
scanf("%d%d",&n,&mod);
fo(i,1,n) scanf("%d",&c[i]);
C[0][0]={0,1};
fo(i,1,60)
{
C[i][0]=C[i][i]={0,1};
fo(j,1,i-1) C[i][j]=C[i-1][j-1]+C[i-1][j];
}
ans={0,0};
fo(len,1,5)
if (clen_sum[len])
{
int sum=60/len;
memset(f,0,sizeof(f));
f[0][sum]={0,1};
fo(i,0,n-1)
{
fo(j,0,sum)
{
int lim=c[i+1]/len+((c[i+1]%len)>0);
fo(k,lim,j)
f[i+1][j-k]=f[i+1][j-k]+(f[i][j]*C[j][k]);
}
}
ans=ans+(f[n][0]*clen_sum[len]);
}
printf("%lld\n",(ans.x+mod)%mod);
// if (ans.y)
// cout<<ans.y<<" !"<<endl;
}
}