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;
	}
}
posted @ 2024-07-28 16:25  gmh77  阅读(4)  评论(0编辑  收藏  举报