SPOJ TRANSP2

发现第二个矩阵写出来实际上相当于把前\(a\)位和后\(b\)位的优先级换了一下写出来。求的最小交换次数就是\(2^{a+b}-环数\)。在这里可以把一个环看成一个等价类,一个数循环移位\(a\)位之后得到的数和他本身等价。

这就转化成一个burnside引理题了。有一个长度为\(a+b\)的环,每位\(0\)\(1\),循环移位\(a\)次等价。后面就推一推就行了。一个排列的环的计数看为\(i\)的一个变换得到\(p_i\),一个环看做一个等价类。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
#define N 1000002
typedef long long ll;
const int p=1000003;
int T,prim[N],cnt,fi[N],low[N],pi[N],ci[N],tot,ans,bin[N],t,G;
bool isp[N];
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
inline int ksm(int d,int k){int ret=1;while(k){if(k&1)ret=1ll*ret*d%p;d=1ll*d*d%p;k>>=1;}return ret;}
inline void upd(int &x,int y){x+=x+y>=p?y-p:y;}
void dfs(int dep,int pr)
{
	if(dep==tot+1)
	{
		int te=1ll*bin[t*pr]*fi[G/pr]%p;
		upd(ans,te);return;
	}
	for(int i=0;i<=ci[dep];i++)dfs(dep+1,pr),pr*=pi[dep];
}
int main()
{
	for(int i=2;i<=1000000;i++)isp[i]=1;fi[1]=1;low[1]=1;
	for(int i=2;i<=1000000;i++)
	{
		if(isp[i])prim[++cnt]=i,fi[i]=i-1,low[i]=i;
		for(int j=1;j<=cnt&&prim[j]*i<=1000000;j++)
		{isp[prim[j]*i]=0;low[prim[j]*i]=prim[j];
			if(i%prim[j]==0){fi[prim[j]*i]=prim[j]*fi[i];break;}
			fi[prim[j]*i]=fi[prim[j]]*fi[i];
		}
	}
	bin[0]=1;for(int i=1;i<=1000000;i++)bin[i]=2*bin[i-1]%p;
	scanf("%d",&T);
	while(T--)
	{
		int a,b;scanf("%d%d",&a,&b);
		if(a==0||b==0){puts("0");continue;}
		t=gcd(a,b),G=(a+b)/t;
		int x=G;ans=tot=0;
		while(x>1)
		{
			int td=low[x];pi[++tot]=td;ci[tot]=0;
			while(x%td==0)x/=td,ci[tot]++;
		}
		dfs(1,1);ans=1ll*ans*ksm(G,p-2)%p;
		printf("%d\n",(bin[a+b]-ans+p)%p);
	}
}
posted @ 2021-04-24 15:07  Lebron_Durant  阅读(66)  评论(0)    收藏  举报