题意:给你一个$2a\times2b$的矩阵,将$1...n$中的数依次从左到右,从上往下填到矩阵里,再把矩阵转置,然后把所有数从左到右,从上往下拿出来得到一个新的排列$A$。你现在每次可以交换两个数,问你从$1...n$变成排列$A$最少要进行多少次操作。
询问次数$\le400000,a+b\le 10^6$
题解:首先我们可以找到所有的循环节,如果一个循环节中有$x$个数,需要交换$x-1$次。所以我们只需要求出循环节的个数$k$,那么答案就是$2^{a+b}-k$。
如何求出循环节的个数呢?假设$a=5,b=3$,考虑元素$(12,1)$,其二进制表示为$(01010,001)$,它原来的位置是$01010\ 001$,新位置是$001\ 01010$。相当于将每个数的位置二进制向右移动$b$位。
所以,我们可以令$gcd(a,b)=g$,将$g$个数分成一组,相当于用$2^g$种颜色去染$a+b\over g$个珠子,就又变成了POJ2154。

#include <cstring>
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll P=1000003;
const int N=2000000;
int T,a,b,n,m,g,num;
ll ans;
ll pw[N+10];
int cnt[100],p[100];
int phi[N+10],np[N+10],pri[N],xp[N+10],mn[N+10];
inline ll pm(ll x,int y)
{
	ll z=1;
	while(y)
	{
		if(y&1)	z=z*x%P;
		x=x*x%P,y>>=1;
	}
	return z;
}
int gcd(int a,int b)
{
	return (!b)?a:gcd(b,a%b);
}
void dfs(int x,int d)
{
	if(x==m+1)
	{
		ans=(ans+pw[g*d]*phi[n/d])%P;
		return ;
	}
	for(int i=0;i<=cnt[x];i++,d*=p[x])	dfs(x+1,d);
}
inline void init()
{
	phi[1]=1;
	int i,j,p;
	for(pw[0]=i=1;i<=N;i++)	pw[i]=(pw[i-1]<<1)%P;
	for(i=2;i<=N;i++)
	{
		if(!np[i])	pri[++num]=i,mn[i]=i,xp[i]=1,phi[i]=i-1;
		for(j=1;j<=num&&i*pri[j]<=N;j++)
		{
			p=pri[j],np[i*p]=1,mn[i*p]=p;
			if(i%p==0)
			{
				phi[i*p]=phi[i]*p,xp[i*p]=xp[i]+1;
				break;
			}
			phi[i*p]=phi[i]*(p-1),xp[i*p]=1;
		}
	}
}
inline void work()
{
	scanf("%d%d",&a,&b),ans=m=0;
	if(!a||!b)
	{
		puts("0");
		return ;
	}
	g=gcd(a,b),n=(a+b)/g;
	int t=n;
	while(t!=1)
	{
		p[++m]=mn[t],cnt[m]=xp[t];
		while(mn[t]==p[m])	t/=p[m];
	}
	dfs(1,1);
	printf("%lld\n",(pw[a+b]-ans*pm(n,P-2)%P+P)%P);
}
int main()
{
	init();
	scanf("%d",&T);
	while(T--)	work();
	return 0;
}//1 2 30000

相关文章:

  • 2021-04-23
  • 2022-01-19
  • 2022-12-23
  • 2022-12-23
  • 2021-06-01
  • 2022-12-23
猜你喜欢
  • 2021-09-16
  • 2022-01-31
  • 2022-12-23
  • 2021-12-14
  • 2021-12-02
  • 2021-12-17
  • 2021-09-28
相关资源
相似解决方案