暑假集训单切赛第一场 CF 266E More Queries to Array(线段树+二项式展开式)

比赛时,第二题就是做的这个,当时果断没仔细考虑,直接用线段树暴力求。结果易想而知,超时了。

比赛后搜了搜题解,恍然大悟。

思路:显然用线段树,但是由于每次查询都会有变,所以不可能存储题目中的式子。
   这里要注意:k的值非常小,所以应该是将式子按二项式定理展开
   (i-L+1)^k=(i+(1-L))^k
   展开之后可以发现:我们可以在节点存储ai*i,ai*i^2,ai*i^3,ai*i^4,ai*i^5 (L<=i<=R)的累加和。
   至于关于(1-L)^j(j=0~5)可以预先枚举所有的L,预先处理一下用数组存储。
   展开式要用到组合,所以组合也要预先处理一下。
  
   当更新时,我们发现l<=i<=r区间中所有的ai都变为x,
   累加和分别为x*(L+...+R),x*(L^2+...+R^2),x*(L^3+...+R^3),...,x*(L^5+...+R^5)
   因此我们也要预先处理1^j+2^j+...+n^j的和,存到一个数组中去

 

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <queue>

using namespace std;
const long long mod=1000000007;
const int maxn=4*100010;
const int maxnN=100005;
int n,m,k;
int L,R;
long long ans;
int C[6][6];
long long S[maxnN][6];    //S[i][j]存储(1-i)^j的值
long long LMi[maxnN][6];  //LMi[i][j]存储i^j的值,1<=i<=n,即ai*i^j中的i^j
long long sum[maxnN][6];  //sum[i][j] 表示1^j+2^j……+i^j的值
struct Node {
    int left,right;
    long long pow0,pow1,pow2,pow3,pow4,pow5;   //powj: ai*i^j的和,j=0~5
    bool lazy;
    int add;
} tree[maxn];

void init1() {
    C[0][0]=1;
    for(int i=1; i<=5; i++) {
        C[i][0]=1;
        C[i][i]=1;
        for(int j=1; j<i; j++) {
            C[i][j]=C[i-1][j-1]+C[i-1][j];  
        }
    }
}

void init2() {
    memset(sum,0,sizeof(sum));
    for(int i=1; i<=n; i++) {
        S[i][0]=1;
        LMi[i][0]=1;
        sum[i][0]=i;
        for(int j=1; j<=5; j++) {
            LMi[i][j]=(LMi[i][j-1]*i)%mod;
            S[i][j]=(S[i][j-1]*(1-i)%mod+mod)%mod;
            sum[i][j]=(sum[i-1][j]+LMi[i][j])%mod;
        }
    }
}

void pushUp(int rt) {
    tree[rt].pow0=(tree[rt<<1].pow0+tree[rt<<1|1].pow0)%mod;
    tree[rt].pow1=(tree[rt<<1].pow1+tree[rt<<1|1].pow1)%mod;
    tree[rt].pow2=(tree[rt<<1].pow2+tree[rt<<1|1].pow2)%mod;
    tree[rt].pow3=(tree[rt<<1].pow3+tree[rt<<1|1].pow3)%mod;
    tree[rt].pow4=(tree[rt<<1].pow4+tree[rt<<1|1].pow4)%mod;
    tree[rt].pow5=(tree[rt<<1].pow5+tree[rt<<1|1].pow5)%mod;
}

void build(int left,int right,int rt) {
    tree[rt].left=left;
    tree[rt].right=right;
    tree[rt].lazy=false;
    tree[rt].add=0;
    if(left==right) {
        scanf("%I64d",&tree[rt].pow0);
        tree[rt].pow1=(tree[rt].pow0*LMi[left][1])%mod;
        tree[rt].pow2=(tree[rt].pow0*LMi[left][2])%mod;
        tree[rt].pow3=(tree[rt].pow0*LMi[left][3])%mod;
        tree[rt].pow4=(tree[rt].pow0*LMi[left][4])%mod;
        tree[rt].pow5=(tree[rt].pow0*LMi[left][5])%mod;
        return;
    }
    int mid=(left+right)>>1;
    build(left,mid,rt<<1);
    build(mid+1,right,rt<<1|1);
    pushUp(rt);
}

void pushDown(int rt);
void update(int l,int r,int value,int rt) {
    if(l==tree[rt].left && tree[rt].right==r) {
        tree[rt].lazy=true;
        tree[rt].add=value;
        tree[rt].pow0=value*(((sum[r][0]-sum[l-1][0])%mod+mod)%mod);
        tree[rt].pow1=value*(((sum[r][1]-sum[l-1][1])%mod+mod)%mod);
        tree[rt].pow2=value*(((sum[r][2]-sum[l-1][2])%mod+mod)%mod);
        tree[rt].pow3=value*(((sum[r][3]-sum[l-1][3])%mod+mod)%mod);
        tree[rt].pow4=value*(((sum[r][4]-sum[l-1][4])%mod+mod)%mod);
        tree[rt].pow5=value*(((sum[r][5]-sum[l-1][5])%mod+mod)%mod);
        return;    
    }
    pushDown(rt);

    int mid=(tree[rt].left+tree[rt].right)>>1;
    if(r<=mid) {
        update(l,r,value,rt<<1);
    } else if(l>mid) {
        update(l,r,value,rt<<1|1);
    } else {
        update(l,mid,value,rt<<1);
        update(mid+1,r,value,rt<<1|1);
    }
    pushUp(rt);

}

void pushDown(int rt) {
    if(tree[rt].lazy && tree[rt].left!=tree[rt].right) {
        tree[rt<<1].lazy=tree[rt<<1|1].lazy=true;
        long long tmp=tree[rt].add;

        tree[rt<<1].add=tree[rt].add;
        tree[rt<<1|1].add=tree[rt].add;

        int mid=(tree[rt].left+tree[rt].right)>>1;
        //这里可以直接调用update方法对儿子进行更新
        update(tree[rt].left,mid,tmp,rt<<1);
        update(mid+1,tree[rt].right,tmp,rt<<1|1);  

        tree[rt].add=0;
        tree[rt].lazy=false;
    }
}



long long query(int l,int r,int rt,int i) {
    if(l==tree[rt].left && tree[rt].right==r) {
        if(i==0)
            return tree[rt].pow0;
        else if(i==1)
            return tree[rt].pow1;
        else if(i==2)
            return tree[rt].pow2;
        else if(i==3)
            return tree[rt].pow3;
        else if(i==4)
            return tree[rt].pow4;
        else if(i==5)
            return tree[rt].pow5;
    }
    pushDown(rt);

    int mid=(tree[rt].left+tree[rt].right)>>1;
    if(r<=mid) {
        return query(l,r,rt<<1,i)%mod;
    } else if(l>mid) {
        return query(l,r,rt<<1|1,i)%mod;
    } else {
        return (query(l,mid,rt<<1,i)+query(mid+1,r,rt<<1|1,i))%mod;
    }
}

int main() {
    int x;
    char ch[5];

    scanf("%d%d",&n,&m);
    init1();
    init2();

    build(1,n,1);
    for(int i=1; i<=m; i++) {
        scanf("%s",ch);
        if(ch[0]=='=') {
            scanf("%d%d%d",&L,&R,&x);
            update(L,R,x,1);
        } else {
            scanf("%d%d%d",&L,&R,&k);
            ans=0;

            //for(int i=0;i<=k;i++){
            for(int i=k; i>=0; i--) {
                //ans=((ans+((query(L,R,1,i)*C[k][i]%mod)*S[L][k-i]%mod))%mod+mod)%mod;
                ans=(ans+((query(L,R,1,i)*C[k][i]%mod)*S[L][k-i]%mod))%mod;

            }

            printf("%I64d\n",ans);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/chenxiwenruo/p/3285535.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值