zoukankan      html  css  js  c++  java
  • CodeForces 407C 组合数学(详解)

    题面:

      http://codeforces.com/problemset/problem/407/C

      一句话题意:给一个长度为n的序列g,m次操作,每次操作(l,r,k)表示将g[l]~g[r]的每个数g[j](l<=j<=r)加上c(j-l+k,k),输出经过m操作后的最终序列(mod 1e9+7)(n,m<=1e5,k<=100)。

    题解:

      首先看到这个题瞬间想到数据结构,但发现一次修改操作中每个点的增加值都不同后果断放弃。又因为发现这题只有一次询问,就考虑能不能先将每次操作存下来,最后再进行统一递推。又看到了k好小。。于是就可以乱搞了!

      我们先考虑组合数的递推,c(n,m)=c(n-1,m)+c(n-1,m-1)。那么观察操作,假设我们在修改g[x],并且x>l,那么g[x-1]已经修改完了,考虑g[x-1]的增加值为c(x-1-l+k,k),而g[x]的值增加了c(x-l+k,k),又因为c(x-l+k,k)=c(x-l-1,k)+c(x-l-1,k-1),但是,显然只存下每个点的c(x-l+k,k)和每个点的c(x-l+k,k-1)是远远不够的,因为这样的话就只能推出c(x-l+k+1,k),而无法推出c(x-l+k+1,k-1),接着就无法推出c(x-l+k+2,k),等于说这次操作就无法递推完。因此我们只要在每一次操作的l处处理出c(k,0~k),就可以做到递推出每次操作对于每个数的增加值,欸那这有什么用呢,欸当然有用了!又因为加法有交换律和结合律,所以我们只要在每个l上计算好,在r+1处减去,就可以O(n)递推出整个序列!因为每在一个l处要处理c(k,0~k),处理m次,所以操作的总复杂度为O(mk),最终递推每推一步都要推k次组合数。因此整套代码的总复杂度为O(mk+nk)!!!!!

      如果还有不懂的那就看代码然后感性理解一下qwq

      P.S. 对于组合数我们是要处理阶乘的逆元(inv)的,有一个O(n)递推1~n逆元的方法:

        首先我们考虑如果知道了x+1~n的阶乘的inv,如何得到x!的inv。。

        根据逆元的定义:n!*inv(n!)=(n-1)!*n*inv(n!)=1=(n-1)!*inv((n-1)!),,欸所以inv((n-1)!)=inv(n!)*n

    代码:

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 typedef long long ll;
     6 typedef double dd;
     7 const int maxn=1e5+10;
     8 const ll p=1e9+7;
     9 ll fac[maxn],inv[maxn],g[maxn],ad[maxn][110];
    10 int n,m;
    11 
    12 ll qpow(ll x,ll b){
    13     ll sum=1;
    14     while(b){
    15         if(b&1) sum=sum*x%p;
    16         x=x*x%p; b>>=1;
    17     }
    18     return sum;
    19 }
    20 
    21 void init(){
    22     fac[0]=1;
    23     for(int i=1;i<=100000;i++)
    24         fac[i]=(ll)i*fac[i-1]%p;
    25     inv[100000]=qpow(fac[100000],p-2);
    26     for(int i=100000-1;i>=0;i--)
    27         inv[i]=(ll)inv[i+1]*(i+1)%p;    
    28 }
    29 
    30 ll c(int x,int y){
    31     if(x<y) return 0;
    32     return fac[x]*inv[y]%p*inv[x-y]%p;    
    33 }
    34 
    35 int main(){
    36     scanf("%d%d",&n,&m);
    37     init();    
    38     for(int i=1;i<=n;i++)
    39         scanf("%lld",&g[i]);
    40     
    41     int l,r,k;
    42     for(int j=1;j<=m;j++){
    43         scanf("%d%d%d",&l,&r,&k);
    44         for(int i=0;i<=k;i++)
    45             ad[l][i]=(ad[l][i]+c(k,k-i))%p,
    46             ad[r+1][i]=(ad[r+1][i]-c(r+1-l+k,k-i)+p)%p;    
    47     }
    48     
    49     for(int i=1;i<=n;i++)
    50         for(int j=100;j>=0;j--)
    51                 ad[i][j]=(ad[i][j]+ad[i-1][j]+ad[i-1][j+1]+p)%p;
    52 
    53     
    54     for(int i=1;i<=n;i++)
    55         printf("%lld ",(g[i]+ad[i][0]+p)%p);    
    56 
    57     return 0;    
    58 }
  • 相关阅读:
    centos npm run build 报错
    python base64
    Emacs 常用命令
    linux 删除文件腾出空间 遇到的问题
    网速查看工具
    linux 查看当前文件夹下的文件大小
    Docker 私有仓库push
    Harbor:Http: server gave HTTP response to HTTPS client & Get https://192.168.2.119/v2/
    docker 私有仓库搭建
    linux 修改时间
  • 原文地址:https://www.cnblogs.com/tang666/p/8759194.html
Copyright © 2011-2022 走看看