zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    vfork与fork的区别
    常见的六种设计模式以及应用场景
    Java中常见的集合类比较
    排序——总结
    排序——交换排序
    排序——选择排序
    排序——归并排序
    排序——基数排序
    排序——插入排序
    设计模式
  • 原文地址:https://www.cnblogs.com/jz-597/p/14706332.html
Copyright © 2011-2022 走看看