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);
}
}