[组合数][差分约束][前缀和] Jzoj P5922 sequence
题解
- 组合数有一个简单的性质,C(n,k)=C(n-1,k-1)+C(n-1,k),定眼一看这不就是杨辉三角吗
- 解释一下为什么吧,现在把n分成两组分别是n-1和1,一共选k个方案数数多少,显然也就是在n-1个里选k个或在n-1个里选k-1给在加上另一组的一个
- 那么这样的话,暴力修改显然不行,思考一下,每两次操作之间是没有影响的,可以考虑一下递推来求
- 然后用差分约束,考虑一下怎么递推,由上面的公式可以发现,也就是c[i-1][j]+c[i-1][j-1]
- 那么再下一个会发现c[i-2][j]+c[i-2][j-1]+c[i-2][j-1]+c[i-2][j-2]
- 这样的话,仅仅只保留k-1是不行的,那么就可以保留0~k,然后差分约束的右端点+1也是保留0~k
- 然后计算一波递推式,答案就是b[i][0]
代码
1 #include<cstdio> 2 #include<cstring> 3 #include<string> 4 #include<algorithm> 5 #define mo 1000000007 6 #define N 500030 7 #define M 30 8 using namespace std; 9 typedef long long ll; 10 ll c[N][M],b[N][M],n,m,l,r,k; 11 void init() 12 { 13 c[0][0]=1; 14 for (ll i=1;i<=500020;i++) 15 for (ll j=0;j<=i&&j<=21;j++) 16 c[i][j]=(j!=0)?(c[i-1][j]+c[i-1][j-1])%mo:1; 17 } 18 int main() 19 { 20 freopen("sequence.in","r",stdin),freopen("sequence.out","w",stdout); 21 init(); 22 scanf("%lld%lld",&n,&m); 23 for (ll i=1;i<=m;i++) 24 { 25 scanf("%lld%lld%lld",&l,&r,&k); 26 for(ll j=0;j<=k;j++) b[l][j]=(b[l][j]+c[k][k-j])%mo,b[r+1][j]=(b[r+1][j]-c[k+r-l+1][k-j])%mo; 27 } 28 for (ll i=1;i<=n;i++) 29 for (ll j=21;j>=1;j--) 30 b[i+1][j-1]=(b[i+1][j-1]+b[i][j]+b[i][j-1])%mo; 31 for(ll i=1;i<=n;i++) printf("%lld\n",(b[i][0]%mo+mo)%mo); 32 return 0; 33 }