Every one will meet some difficult

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

###Description
求方程∑i=1mxi&lt;=s\sum_{i=1}^{m}xi&lt;=si=1mxi<=s且对于i=1~n,xi&lt;=txi&lt;=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=0n(1)iCniCstim
大概思路是容斥然后用组合数计算。

####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)niCnif[x+i]
证明的话可以归纳,暴力展开证明
那么我们设Ans=(−1)nansAns=(-1)^{n}ansAns=(1)nans,f[i]=Cs−txmf[i]=C_{s-tx}^{m}f[i]=Cstxm
可以发现Ans=∑i=0n(−1)n+iCnif[i]Ans=\sum_{i=0}^{n}(-1)^{n+i}C_n^if[i]Ans=i=0n(1)n+iCnif[i]
=∑i=0n(−1)n−iCnif[i]=\sum_{i=0}^{n}(-1)^{n-i}C_n^if[i]=i=0n(1)niCnif[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=0m(1)mi[im]xi
只是因为递推式相等罢了,因为不会打就用排列数代替下降幂了。。。

第二类斯特林数{nm}{\{_n^m\}}{nm}表示将m个可区分元素划分成n个不可区分的集合的方案数
也有公式xm=∑i=0m{im}Pxix^m=\sum_{i=0}^{m}{\{_i^m\}}P_x^ixm=i=0m{im}Pxi
考虑一个组合问题"将m个可区分的球扔到n个可区分的箱子里的方案数"
显然答案是nmn^mnm
再考虑枚举有球的箱子,假设有i个,那么我们需要将m个球划分成i个集合,然后对集合分配箱子。因为可区分所以是排列数。

####Part 4
回到原问题f[x]=Cs−txmf[x]=C_{s-tx}^mf[x]=Cstxm
=1m!Ps−txm={1\over m!}P_{s-tx}^{m}=m!1Pstxm
=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=0m(1)mi[im](stx)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=0m(1)mi[im]j=0i(1)jCijsijtjxj
=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=0mxji=jm(1)mi+j[im]CijSijtj
设后面那个∑中的东西为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=0mxia[i]
=∑i=0ma[i]∑j=0i{ji}Pxj=\sum_{i=0}^{m}a[i]\sum_{j=0}^{i}\{_j^i\}P_x^j=i=0ma[i]j=0i{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=0ma[i]j=0i{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=0mCxji=jma[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]Cxin
证明也可以暴力归纳。。。。

于是我们得出了Ans=∑i=0mb[i]C0i−nAns=\sum_{i=0}^{m}b[i]C_0^{i-n}Ans=i=0mb[i]C0in
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=nm{ni}j=im(1)mj+iCji[jm]Sjiti
由于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)t1)n(x+1)snt
考虑写成[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}[xmn](x(x+1)t1)n(x+1)snt
就只用保留前m-n项,直接暴力就好了
还可以用多项式取模做到O((m-n) log m-n log n)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值