听了杜教的直播后知道了怎么做,有两种方法,一种构造函数(现在太菜了,听不懂,以后再补),一种容斥原理。
知识补充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); } }
HDU 6397(2018多校第8场1001) Character Encoding 容斥
原文:https://www.cnblogs.com/pkgunboat/p/9484230.html