###Description
求方程∑i=1mxi<=s\sum_{i=1}^{m}xi<=s∑i=1mxi<=s且对于i=1~n,xi<=txi<=txi<=t的正整数解数。
n,m<=1e9,nt<=s<=1e18,m-n<=1e3
###Beat the Challeng
####Part 1
答案等于∑i=0n(−1)iCniCs−tim\sum_{i=0}^{n}(-1)^iC_n^iC_{s-ti}^mi=0∑n(−1)iCniCs−tim
大概思路是容斥然后用组合数计算。
####Part 2
我们先来介绍一下n阶差分公式
定义一次差分Δf[i]=f[i+1]−f[i]\Delta f[i]=f[i+1]-f[i]Δf[i]=f[i+1]−f[i]
那么Δnf\Delta^n fΔnf为f经过n次差分之后所得的东西
一个结论:Δnf[x]=∑i=0n(−1)n−iCnif[x+i]\Delta^n f[x]=\sum_{i=0}^{n}(-1)^{n-i}C_n^if[x+i]Δnf[x]=∑i=0n(−1)n−iCnif[x+i]
证明的话可以归纳,暴力展开证明
那么我们设Ans=(−1)nansAns=(-1)^{n}ansAns=(−1)nans,f[i]=Cs−txmf[i]=C_{s-tx}^{m}f[i]=Cs−txm
可以发现Ans=∑i=0n(−1)n+iCnif[i]Ans=\sum_{i=0}^{n}(-1)^{n+i}C_n^if[i]Ans=i=0∑n(−1)n+iCnif[i]
=∑i=0n(−1)n−iCnif[i]=\sum_{i=0}^{n}(-1)^{n-i}C_n^if[i]=i=0∑n(−1)n−iCnif[i]
=Δnf[0]=\Delta^n f[0]=Δnf[0]
####Part 3
两类斯特林数及公式
第一类斯特林数[nm][_n^m][nm]表示将m个可区分元素划分成n个不可区分圆排列的方案数
也有公式Pxm=∑i=0m(−1)m−i[im]xiP_x^m=\sum_{i=0}^{m}(-1)^{m-i}[_i^m]x^iPxm=i=0∑m(−1)m−i[im]xi
只是因为递推式相等罢了,因为不会打就用排列数代替下降幂了。。。
第二类斯特林数{nm}{\{_n^m\}}{nm}表示将m个可区分元素划分成n个不可区分的集合的方案数
也有公式xm=∑i=0m{im}Pxix^m=\sum_{i=0}^{m}{\{_i^m\}}P_x^ixm=i=0∑m{im}Pxi
考虑一个组合问题"将m个可区分的球扔到n个可区分的箱子里的方案数"
显然答案是nmn^mnm
再考虑枚举有球的箱子,假设有i个,那么我们需要将m个球划分成i个集合,然后对集合分配箱子。因为可区分所以是排列数。
####Part 4
回到原问题f[x]=Cs−txmf[x]=C_{s-tx}^mf[x]=Cs−txm
=1m!Ps−txm={1\over m!}P_{s-tx}^{m}=m!1Ps−txm
=1m!∑i=0m(−1)m−i[im](s−tx)i={1\over m!}\sum_{i=0}^{m}(-1)^{m-i}[_i^m](s-tx)^i=m!1i=0∑m(−1)m−i[im](s−tx)i
=1m!∑i=0m(−1)m−i[im]∑j=0i(−1)jCijsi−jtjxj={1\over m!}\sum_{i=0}^{m}(-1)^{m-i}[_i^m]\sum_{j=0}^{i}(-1)^jC_i^js^{i-j}t^jx^j=m!1i=0∑m(−1)m−i[im]j=0∑i(−1)jCijsi−jtjxj
=1m!∑j=0mxj∑i=jm(−1)m−i+j[im]CijSi−jtj={1\over m!}\sum_{j=0}^{m}x^j\sum_{i=j}^{m}(-1)^{m-i+j}[_i^m]C_i^jS^{i-j}t^j=m!1j=0∑mxji=j∑m(−1)m−i+j[im]CijSi−jtj
设后面那个∑中的东西为a[j],那么f[x]=∑i=0mxia[i]f[x]=\sum_{i=0}^{m}x^ia[i]f[x]=∑i=0mxia[i]
我们继续:
f[x]=∑i=0mxia[i]f[x]=\sum_{i=0}^{m}x^ia[i]f[x]=i=0∑mxia[i]
=∑i=0ma[i]∑j=0i{ji}Pxj=\sum_{i=0}^{m}a[i]\sum_{j=0}^{i}\{_j^i\}P_x^j=i=0∑ma[i]j=0∑i{ji}Pxj
=∑i=0ma[i]∑j=0i{ji}Cxjj!=\sum_{i=0}^{m}a[i]\sum_{j=0}^{i}\{_j^i\}C_x^jj!=i=0∑ma[i]j=0∑i{ji}Cxjj!
=∑j=0mCxj∑i=jma[j]{ji}j!=\sum_{j=0}^{m}C_x^j\sum_{i=j}^{m}a[j]\{_j^i\}j!=j=0∑mCxji=j∑ma[j]{ji}j!
令后面那个∑中的式子为b[j],那么我们有f[x]=∑i=0mCxib[i]f[x]=\sum_{i=0}^{m}C_x^ib[i]f[x]=∑i=0mCxib[i]
####Part 5
回到原始式子,我们有Ans=Δnf[0]Ans=\Delta^nf[0]Ans=Δnf[0]
我们又得出了f[x]=∑i=0mCxib[i]f[x]=\sum_{i=0}^{m}C_x^ib[i]f[x]=∑i=0mCxib[i]
接下来我们给出一个结论:
如果f[x]=∑i=0mCxig[i]f[x]=\sum_{i=0}^{m}C_x^ig[i]f[x]=∑i=0mCxig[i]
那么Δnf[x]=∑i=0mg[i]Cxi−n\Delta^nf[x]=\sum_{i=0}^{m}g[i]C_x^{i-n}Δnf[x]=∑i=0mg[i]Cxi−n
证明也可以暴力归纳。。。。
于是我们得出了Ans=∑i=0mb[i]C0i−nAns=\sum_{i=0}^{m}b[i]C_0^{i-n}Ans=∑i=0mb[i]C0i−n
即Ans=b[n]Ans=b[n]Ans=b[n]
代回a和b我们可以得出Ans=n!m!∑i=nm{ni}∑j=im(−1)m−j+iCji[jm]Sj−itiAns={n!\over m!}\sum_{i=n}^{m}\{_n^i\}\sum_{j=i}^{m}(-1)^{m-j+i}C_j^i[_j^m]S^{j-i}t^iAns=m!n!i=n∑m{ni}j=i∑m(−1)m−j+iCji[jm]Sj−iti
由于i和j的差不超过m-n,所以如果我们知道了需要的两类斯特林数可以O((m-n)^2)计算
####Part 6
正常做法可能需要O(m^2),我们需要从组合意义上来理解。
因为将m个元素放入n个集合/圆排列中,我们都需要至少在每一个集合/排列中放一个数。
那么我们元素大于1个的集合/排列不会超过m-n个,可以设F[i][j]表示将i个元素插入j个元素>1的集合/排列的方案数,然后直接Dp。
最终计算时枚举到底有几个元素>1的集合/排列
这样总复杂度就是O((m-n)^2)的了,足以通过本题
zrO 提供此题的wzd Orz
然而外校有人考场切?!
###Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=2*1e3+5,Mo=1e9+7;
int pwr(int x,int y) {
int z=1;
for(;y;y>>=1,x=(ll)x*x%Mo)
if (y&1) z=(ll)z*x%Mo;
return z;
}
int n,m,mn,inv[N],ps[N];
int f[N][N],g[N][N],c[N][N],s[N],su[N];
ll S,T;
int C(int y,int x) {
if (x>2*mn) x=y-x;
return c[y-n][x];
}
int main() {
freopen("success.in","r",stdin);
freopen("success.out","w",stdout);
scanf("%lld%lld%d%d",&S,&T,&n,&m);
mn=m-n;S%=Mo;T%=Mo;
fo(i,0,2*mn) inv[i]=pwr(i,Mo-2);
ps[0]=1;fo(i,1,mn) ps[i]=(ll)ps[i-1]*S%Mo;
fo(i,0,2*mn) {
c[i][0]=1;
fo(j,1,min(i+n,2*mn)) {
c[i][j]=(ll)c[i][j-1]*(i+n-j+1)%Mo;
c[i][j]=(ll)c[i][j]*inv[j]%Mo;
}
}
f[0][0]=g[0][0]=1;
fo(i,1,2*mn)
fo(j,1,i/2) {
f[i][j]=(ll)f[i-1][j]*(i-1)%Mo;
g[i][j]=(ll)g[i-1][j]*j%Mo;
if (i>1) {
(f[i][j]+=(ll)f[i-2][j-1]*(i-1)%Mo)%=Mo;
(g[i][j]+=(ll)g[i-2][j-1]*(i-1)%Mo)%=Mo;
}
}
fo(i,0,mn)
fo(j,0,min(m-(i+n),i+n))
(su[i]+=(ll)f[m-(i+n)+j][j]*C(m,m-(i+n)+j)%Mo)%=Mo;
fo(i,0,mn)
fo(j,0,min(i,n))
(s[i]+=(ll)g[i+j][j]*C(n+i,i+j)%Mo)%=Mo;
int ans=0;
fo(i,0,mn) {
int sum=0,pt=pwr(T,i+n);
fo(j,i,mn) {
int res=(ll)C(n+j,n+i)*su[j]%Mo*ps[j-i]%Mo*pt%Mo;
if ((m-j+i)&1) (sum+=Mo-res)%=Mo;
else (sum+=res)%=Mo;
}
(ans+=(ll)sum*s[i]%Mo)%=Mo;
}
fo(i,n+1,m) ans=(ll)ans*pwr(i,Mo-2)%Mo;
if (n&1) ans=Mo-ans;
printf("%d\n",ans);
return 0;
}
UPD(2019.1.1)
然而这道题被林立打爆了。。。。
考虑原式子的组合意义,相当于有n+1组,前n组有t个,最后一组有s-nt个,从中选出m个数,前n组至少选一个的方案数
生成函数为[xm]((x+1)t−1)n(x+1)s−nt[x^m]((x+1)^t-1)^n(x+1)^{s-nt}[xm]((x+1)t−1)n(x+1)s−nt
考虑写成[xm−n]((x+1)t−1x)n(x+1)s−nt[x^{m-n}]({(x+1)^t-1\over x})^n(x+1)^{s-nt}[xm−n](x(x+1)t−1)n(x+1)s−nt
就只用保留前m-n项,直接暴力就好了
还可以用多项式取模做到O((m-n) log m-n log n)

本文深入探讨了一类复杂的组合数学问题,给出了求特定形式方程正整数解数量的方法。利用容斥原理和组合数计算,引入斯特林数,并通过生成函数进行分析,最终提出了高效的算法解决方案。
2127

被折叠的 条评论
为什么被折叠?



