• HDU 6397(2018多校第8场1001) Character Encoding 容斥


    听了杜教的直播后知道了怎么做,有两种方法,一种构造函数(现在太菜了,听不懂,以后再补),一种容斥原理。

    知识补充1:若x1,x2,.....xn均大于等于0,则x1+x2+...+xn=k的方案数是C(k+m-1,m-1)种(貌似紫书上有,记不太清了)。

    知识补充2:若限制条件为n(即x1,x2....xn均小于n,假设有c个违反,则把k减掉c个n(相当于把c个超过n的数也变成大于等于0的),就可以套用知识1的公式了。

    则最后的答案为sum( (-1)^c * C(m , c) * C(m-1+k-n*c , m-1) );

    这个题貌似要预处理出乘法逆元,不然会TLE。我的lucas定理做法超时了。。。

    借鉴了杜教链接中的代码,O(n)时间预处理:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL mod=998244353;
    const int maxn=203000;
    LL f[maxn],fv[maxn];//f是阶乘,fv是乘法逆元 
    LL quick_power(LL a,LL b){
    	LL ans=1;
    	for(;b;b>>=1){
    		if(b&1)ans=ans*a%mod;
    		a=a*a%mod;
    	}
    	return ans;
    }
    void init(){//初始化 
    	f[0]=1;
    	for(LL i=1;i<maxn;i++)
    		f[i]=(f[i-1]*i)%mod;
    	fv[maxn-1]=quick_power(f[maxn-1],mod-2);
    	for(LL i=maxn-1;i>0;i--){
    		fv[i-1]=fv[i]*i%mod;
    	}
    }
    LL C(LL n,LL m){//这样可以O(1)计算出组合数 
    	if(n<0||m<0||n<m)return 0;
    	return f[n]*(fv[m])%mod*fv[n-m]%mod;
    }
    int main(){
    	LL n,m,k,ans;
    	int T;
    	init();
    	scanf("%d",&T);
    	while(T--){
    		scanf("%lld%lld%lld",&n,&m,&k);
    		LL ans=0;
    		for(int c=0;c*n<=k;c++){//容斥 
    			if(c&1)ans=(ans-C(m,c)*C(k-c*n-1+m,m-1)%mod+mod)%mod;
    			else ans=(ans+C(m,c)*C(k-c*n-1+m,m-1)%mod)%mod;
    		}
    		printf("%lld\n",ans);
    	}
    } 
    

      还是附上超时代码,单次查询是O(lgn)的,如果查询次数比较少可以用这个,可以当作模板。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    LL mod=998244353;
    LL power(LL a){
    	if(a&1)return -1;
    	return 1;
    }
    LL quick_power(LL a,LL b){
    	LL ans=1%mod;
    	while(b){
    		if(b&1){
    			ans=ans*a%mod;
    			b--;
    		}
    		b>>=1;
    		a=a*a%mod; 
    	}
    	return ans;
    } 
    LL C(LL n,LL m){
    	if(m>n)return 0;
    	LL ans=1;
    	for(int i=1;i<=m;i++){
    		LL a=(n+i-m)%mod;
    		LL b=i%mod;
    		ans=ans*(a*quick_power(b,mod-2)%mod)%mod;
    	}
    	return ans;
    }
    LL lucas(LL n,LL m){
    	if(m==0)return 1;
    	return C(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
    }
    int main(){
    	LL n,m,k,ans;
    	int T;
    	scanf("%d",&T);
    	while(T--){
    		ans=0;
    		scanf("%lld%lld%lld",&n,&m,&k);
    		for(LL i=0;i*n<=k;i++){
    			ans=(ans+(((power(i)*lucas(m,i))%mod)*lucas(m-1+k-n*i,m-1))%mod)%mod;
    		}
    		printf("%lld\n",ans);
    	}
    }  
    

      

  • 相关阅读:
    基本数据类型(int, bool, str)
    万恶之源之运算符
    python基础初识
    leetcode 653. Two Sum IV
    leetcode 16 3Sum Closest
    leetcode15 3Sum
    leetcode 1 Two Sum
    【站立会议】第四天
    【站立会议】第三天
    【站立会议】第二天
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/9484230.html
Copyright © 2020-2023  润新知