LYOI#1166. Cat function(线段树)

Cat function

题目描述:



分析:

操作1:将单点覆盖为k
操作2:求区间内任意两个数乘积的总和。
对于操作1:线段树的模板操作,进行单点修改即可。
对于操作2:显然只有一个或两个数的小区间计算答案非常简单,所以直接考虑线段树两个子区间之间的信息将其合并。
我们设每一个区间的乘积总和为该区间的\(ans\)
显然两个子区间的\(ans\)之和并不等于大区间的\(ans\)

举例:

有这两个子区间需要合并
\([1,2][3,4]\)
左区间\(ans_l=1*2=2\)
右区间\(ans_r=3*4=12\)
大区间\(ans_s=1*2+1*3+1*4+2*3+2*4+3*4=35\)
\(所以显然ans_l+ans_r \neq ans_s\)

那么我们在计算合并的\(ans\)时漏掉了什么?
通过比较上面的三个式子不难看出,在计算大区间\(ans\)时比两个子区间多计算了\(1*3\) , \(1*4\) , \(2*3\) , \(2*4\)
而这些情况正是:要计算乘积的两个元素不在同一区间内,所以无法被两个子区间\(ans\)包含。也可以理解为两个子区间的元素需要相互交叉相乘。
这里我们可以通过在原先\(ans\)的基础上加上两个子区间的和的乘积,用来概括刚才没有考虑的部分。

具体代码实现:

代码:

#include<bits/stdc++.h>
#define mod 1000000007
#define MAXN 1000086
#define int long long
using namespace std;

struct T{
int l,r,val;
int ans;
}t[MAXN];

int a[MAXN];
void update(int node){
    t[node].val=(t[node<<1].val+t[node<<1|1].val)%mod;
    t[node].ans=(t[node<<1].ans+t[node<<1|1].ans)%mod;
    t[node].ans=(t[node].ans+(t[node<<1].val*t[node<<1|1].val)%mod)%mod;
}

void build(int l,int r,int node){
    t[node].l=l;
    t[node].r=r;
    if(l==r){
        t[node].ans=0;
        t[node].val=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1);
    update(node);
}

T ask(int l,int r,int node){
    if(l<=t[node].l&&t[node].r<=r){
        return t[node];
    }
    int mid=(t[node].l+t[node].r)>>1;
    T lson={0,0},rson={0,0},kkk={0,0};
    if(l<=mid){
        lson=ask(l,r,node<<1);
    }
    if(r>mid){
        rson=ask(l,r,node<<1|1);
    }
    kkk.val=(lson.val+rson.val)%mod;
    kkk.ans=((lson.ans+rson.ans)%mod+(lson.val*rson.val)%mod)%mod;
    return kkk;
}

void change(int p,int k,int node){
if(t[node].l==t[node].r){
    t[node].val=k;
    return ;
}
int mid=(t[node].l+t[node].r)>>1;
if(p<=mid)
    change(p,k,node<<1);
else
    change(p,k,node<<1|1);
    
    
    
update(node);
}

int n,m,op;

signed main(){
    freopen("segment.in", "r", stdin);
    freopen("segment.out", "w", stdout);
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <= n; i++) {
        scanf("%lld",&a[i]);
    }
    build(1,n,1);
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>op>>x>>y;
        if(op==1){
            change(x,y,1);
        }
        else{
            int cat=ask(x, y, 1).ans;
            printf("%lld\n",cat);
        }
    }

    return 0;
}


猫猫函数

posted @ 2022-08-30 15:39  DAIANZE  阅读(68)  评论(0编辑  收藏  举报