6210. wsm

询问有多少对序列(A,B),满足:(|A|=m),把所有(A_i+B_j)都丢进序列(C)中排序得到(0,1,2,dots,n-1)

(Tle 500,m|nle 10^{12})


考虑暴力搜索的过程:

假设当前到达状态(A={0,1},B={0,2})。此时已经表示了(0dots 3),于是要添加(4)进去。

注意到因为({0,1}*{0,2}={0,1,2,3}),有({0+4,1+4}*{0,2}={4,5,6,7}),于是在(A)后面丢(4,5),变成({0,1,4,5})

当然也可以丢多一些,比如变成({0,1,4,5,8,9,12,13})。这相当于将({0,1})分开很多份,第(k)份加上(4k)

于是可以看成这样的过程:(A,B)轮流操作。假设当前的(A,B)能表示(0dots c-1)。每次选择一个不为(1)的数(d),将当前序列复制(d)份(编号(0dots d-1)),第(k)份总体加上(kc)

然后可以转化这样的问题:设(F(n,m)=sum_{1<d|n} F(m,frac{n}{d}))(ans=F(m,frac{n}{m})+F(frac{n}{m},n))

接下来就简单多了:考虑计算(F(n,m))。设(f_n(c))表示对(n)做除法(c)次变成(1)的方案数。显然这可以分解质因数,容斥,对每个质因数分别计算得到。(ans=sum f_n(c)(f_m(c-1)+2f_m(c)+f_m(c+1)))


using namespace std;
#include <bits/stdc++.h>
#define N 1000005
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
ll n,m;
int p[N],np;
bool inp[N];
void initp(int n=1000000){
	for (int i=2;i<=n;++i){
		if (!inp[i])
			p[++np]=i;
		for (int j=1;j<=np && i*p[j]<=n;++j){
			inp[i*p[j]]=1;
			if (i%p[j]==0)
				break;
		}
	}
}
const int K=105;
ll fac[K],ifac[K];
void initC(int n=100){
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
}
ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
struct Query{
	ll n;
	ll d[K];
	int e[K],k,s;
	ll f[K];
	void work(ll _n){
		n=_n;
		ll t=n;
		k=0;
		for (int i=1;i<=np && t!=1;++i)
			if (t%p[i]==0){
				d[++k]=p[i],e[k]=0;
				while (t%p[i]==0)
					t/=p[i],e[k]++;
			}
		if (t!=1)
			d[++k]=t,e[k]=1;
		s=0;
		for (int i=1;i<=k;++i)
			s+=e[i];
		static ll g[N];
		g[0]=(n==1);
		for (int i=1;i<=s;++i){
			g[i]=1;
			for (int j=1;j<=k;++j)
				g[i]=g[i]*C(e[j]+i-1,i-1)%mo;
		}
		for (int i=0;i<=s;++i){
			f[i]=0;
			for (int j=0;j<=i;++j)
				(f[i]+=g[j]*C(i,j)*(i-j&1?-1:1))%=mo;	
			f[i]=(f[i]+mo)%mo;
		}
	}
	ll askf(int x){
		if (0<=x && x<=s)
			return f[x];
		return 0;
	}
} qn,qm;
int main(){
	freopen("wsm.in","r",stdin);
	freopen("wsm.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	initp();
	initC();
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%lld%lld",&n,&m);
		n/=m;
		if (n==1 || m==1){
			printf("1
");
			continue;
		}
		qn.work(n);
		qm.work(m);
		ll ans=0;
		for (int i=0;i<=qn.s;++i)
			(ans+=qn.askf(i)*(qm.askf(i-1)+qm.askf(i)*2+qm.askf(i+1)))%=mo;
		printf("%lld
",ans);
			
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14706332.html