hdu4578——线段树多种操作

题目链接:https://vjudge.net/problem/HDU-4578#author=hzoi2017_csm

Description

Yuanfang is puzzled with the question below:
There are n integers, a 1, a 2, …, a n. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between a x and a y inclusive. In other words, do transformation a k<---a k+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between a x and a y inclusive. In other words, do transformation a k<---a k×c, k = x,x+1,…,y.
Operation 3: Change the numbers between a x and a y to c, inclusive. In other words, do transformation a k<---c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between a x and a y inclusive. In other words, get the result of a x p+a x+1 p+…+a y p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him.

Input

There are no more than 10 test cases.
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
Each the following m lines contains an operation. Operation 1 to 3 is in this format: "1 x y c" or "2 x y c" or "3 x y c". Operation 4 is in this format: "4 x y p". (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
The input ends with 0 0.

Output

For each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.

Sample Input

5 5
3 3 5 7
1 2 4 4
4 1 5 2
2 2 5 8
4 3 5 3
0 0

Sample Output

307
7489

 题意

给你一段长度为n的序列(初始化为0),总共有m次操作。

操作1:[x,y]区间内的数都加上c

操作2:[x,y]区间内的数都乘上c

操作3:把[x,y]区间内的数都变成c

操作4:查询[x,y]区间内的每个数的p次方的和。

题解

这个题关键在于要发现p的范围很小(1<=p<=3),因此我们直接分别维护1次方的值,2次方的值,3次方的值就行了。

然后我们考虑区间修改操作,首先开三个懒标记,对应三种操作,我们很容易发现赋值的优先级是最高的,因此我们每次先判断是否是赋值操作,如果有赋值操作,直接进行修改(不考虑前面的操作),之后我们考虑乘法和加法操作,如果前面既有乘法操作又有加法操作,我们可以先考虑乘法,很明显先乘再加不会出现多加的情况,类似于a*c+b的处理。

所以我们一次方的值变成原来的值乘于乘法标记然后再加上加法标记的值,对于二次方我们可以发现:(a*c+b)^2  -> a^2*c^2 + 2*a*c*b+b^2,a^2即原来的二次方的值。三次方类比即可求出。

这个题不难想,但是挺复杂的,不过想清楚也就不难了,就pushdown懒标记方和修改的时候边复杂了。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0)*4
#define e 2.718281828
#define rp(rt,s,t) for (rt = (s); rt <= (t); rt++)
#define RP(rt,s,t) for (rt = (t); rt >= (s); rt--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
inline int read()
{
    int a=0,b=1;
    char val=getchar();
    while(val<'0'||val>'9')
    {
        if(val=='-')
            b=-1;
        val=getchar();
    }
    while(val>='0'&&val<='9')
    {
        a=(a<<3)+(a<<1)+val-'0';
        val=getchar();
    }
    return a*b;
}
const int mod=10007;
const int N = 1e5+7;
struct node{
    int l,r;
    int lazy1,lazy2,lazy3;//对应三种操作的懒标记
    int sum1,sum2,sum3;//维护一次方,二次方,三次方的值
}tree[N<<2];
void pushdown(int rt)//下传,最复杂的部分
{
    if(tree[rt].l == tree[rt].r) return;
    if(tree[rt].lazy3 != 0)//先处理有等号标记的情况
    {
        //直接初始化儿子加和乘两种情况的lazy标记
        tree[rt<<1].lazy3 = tree[(rt<<1)|1].lazy3 = tree[rt].lazy3;
        tree[rt<<1].lazy1 = tree[(rt<<1)|1].lazy1 = 0;
        tree[rt<<1].lazy2 = tree[(rt<<1)|1].lazy2 = 1;
        
        //更新左右儿子维护的值
        tree[rt<<1].sum1 = (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt<<1].lazy3%mod;
        tree[rt<<1].sum2 = (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt<<1].lazy3%mod*tree[rt<<1].lazy3%mod;
        tree[rt<<1].sum3 = (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt<<1].lazy3%mod*tree[rt<<1].lazy3%mod*tree[rt<<1].lazy3%mod;
        tree[(rt<<1)|1].sum1 = (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[(rt<<1)|1].lazy3%mod;
        tree[(rt<<1)|1].sum2 = (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[(rt<<1)|1].lazy3%mod*tree[(rt<<1)|1].lazy3%mod;
        tree[(rt<<1)|1].sum3 = (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[(rt<<1)|1].lazy3%mod*tree[(rt<<1)|1].lazy3%mod*tree[(rt<<1)|1].lazy3%mod;
        
        //不要忘记清空标记
        tree[rt].lazy3 = 0;
    }
    if(tree[rt].lazy1 != 0 || tree[rt].lazy2 != 0)//有加号标记或者乘号标记
    {
        //更新左儿子的两种标记
        tree[rt<<1].lazy1 = ( tree[rt].lazy2*tree[rt<<1].lazy1%mod + tree[rt].lazy1 )%mod;
        tree[rt<<1].lazy2 = tree[rt<<1].lazy2*tree[rt].lazy2%mod;

        //更新左儿子的值
        int sum1,sum2,sum3;
        sum1 = (tree[rt<<1].sum1*tree[rt].lazy2%mod + (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt].lazy1%mod)%mod;
        sum2 = (tree[rt].lazy2 * tree[rt].lazy2 % mod * tree[rt<<1].sum2 % mod + 2*tree[rt].lazy1*tree[rt].lazy2%mod * tree[rt<<1].sum1%mod + (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt].lazy1%mod*tree[rt].lazy1%mod)%mod;
        sum3 = tree[rt].lazy2 * tree[rt].lazy2 % mod * tree[rt].lazy2 % mod * tree[rt<<1].sum3 % mod;
        sum3 = (sum3 + 3*tree[rt].lazy2 % mod * tree[rt].lazy2 % mod * tree[rt].lazy1 % mod * tree[rt<<1].sum2) % mod;
        sum3 = (sum3 + 3*tree[rt].lazy2 % mod * tree[rt].lazy1 % mod * tree[rt].lazy1 % mod * tree[rt<<1].sum1) % mod;
        sum3 = (sum3 + (tree[rt<<1].r - tree[rt<<1].l + 1)*tree[rt].lazy1%mod * tree[rt].lazy1 % mod * tree[rt].lazy1 % mod) % mod;
        tree[rt<<1].sum1 = sum1;
        tree[rt<<1].sum2 = sum2;
        tree[rt<<1].sum3 = sum3;

        //更新右儿子的两种标记
        tree[(rt<<1)|1].lazy1 = ( tree[rt].lazy2*tree[(rt<<1)|1].lazy1%mod + tree[rt].lazy1 )%mod;
        tree[(rt<<1)|1].lazy2 = tree[(rt<<1)|1].lazy2 * tree[rt].lazy2 % mod;

        //更新右儿子的值
        sum1 = (tree[(rt<<1)|1].sum1*tree[rt].lazy2%mod + (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[rt].lazy1%mod)%mod;
        sum2 = (tree[rt].lazy2 * tree[rt].lazy2 % mod * tree[(rt<<1)|1].sum2 % mod + 2*tree[rt].lazy1*tree[rt].lazy2%mod * tree[(rt<<1)|1].sum1%mod + (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[rt].lazy1%mod*tree[rt].lazy1%mod)%mod;
        sum3 = tree[rt].lazy2 * tree[rt].lazy2 % mod * tree[rt].lazy2 % mod * tree[(rt<<1)|1].sum3 % mod;
        sum3 = (sum3 + 3*tree[rt].lazy2 % mod * tree[rt].lazy2 % mod * tree[rt].lazy1 % mod * tree[(rt<<1)|1].sum2) % mod;
        sum3 = (sum3 + 3*tree[rt].lazy2 % mod * tree[rt].lazy1 % mod * tree[rt].lazy1 % mod * tree[(rt<<1)|1].sum1) % mod;
        sum3 = (sum3 + (tree[(rt<<1)|1].r - tree[(rt<<1)|1].l + 1)*tree[rt].lazy1%mod * tree[rt].lazy1 % mod * tree[rt].lazy1 % mod) % mod;
        tree[(rt<<1)|1].sum1 = sum1;
        tree[(rt<<1)|1].sum2 = sum2;
        tree[(rt<<1)|1].sum3 = sum3;
        tree[rt].lazy1 = 0;
        tree[rt].lazy2 = 1;
    }
}
void pushup(int rt){//上传根据子节点信息更新父节点
    if(tree[rt].l==tree[rt].r) return ;//叶子节点返回
    tree[rt].sum1=(tree[rt<<1].sum1+tree[rt<<1|1].sum1)%mod;
    tree[rt].sum2=(tree[rt<<1].sum2+tree[rt<<1|1].sum2)%mod;
    tree[rt].sum3=(tree[rt<<1].sum3+tree[rt<<1|1].sum3)%mod;
}
void build(int l,int r,int rt){
    tree[rt].l=l,tree[rt].r=r;
    tree[rt].lazy1=tree[rt].lazy3=0;
    tree[rt].lazy2=1;
    tree[rt].sum1=tree[rt].sum2=tree[rt].sum3=0;//节点信息的初始化
    int m=(l+r)>>1;
    if(l==r) return ;
    build(lson);
    build(rson);
}
void update(int rt,int L,int R,int opt,int val){
    if(L==tree[rt].l&&R==tree[rt].r){
        val%=mod;
        if(opt==1){//加
            tree[rt].lazy1 += val;
            tree[rt].lazy1 %= mod;
            tree[rt].sum3 = (tree[rt].sum3 + 3*tree[rt].sum2%mod*val%mod + 3*tree[rt].sum1%mod*val%mod*val%mod + (tree[rt].r - tree[rt].l + 1)*val%mod*val%mod*val%mod)%mod;
            tree[rt].sum2 = (tree[rt].sum2 + 2*tree[rt].sum1%mod*val%mod + (tree[rt].r - tree[rt].l + 1)*val%mod*val%mod)%mod;
            tree[rt].sum1 = (tree[rt].sum1 + (tree[rt].r - tree[rt].l + 1)*val%mod)%mod;
        }
        else if(opt==2){//乘
            tree[rt].lazy1 = tree[rt].lazy1*val%mod;
            tree[rt].lazy2 = tree[rt].lazy2*val%mod;
            tree[rt].sum1 = tree[rt].sum1*val%mod;
            tree[rt].sum2 = tree[rt].sum2*val%mod*val%mod;
            tree[rt].sum3 = tree[rt].sum3*val%mod*val%mod*val%mod;
        }
        else{//赋值
            tree[rt].lazy1 = 0;
            tree[rt].lazy2 = 1;
            tree[rt].lazy3 = val%mod;
            tree[rt].sum1 = val*(tree[rt].r - tree[rt].l + 1)%mod;
            tree[rt].sum2 = val*(tree[rt].r - tree[rt].l + 1)%mod*val%mod;
            tree[rt].sum3 = val*(tree[rt].r - tree[rt].l + 1)%mod*val%mod*val%mod;
        }
        return ;
    }
    pushdown(rt);
    int m=(tree[rt].l+tree[rt].r)>>1;
    if(R<=m) update(rt<<1,L,R,opt,val); 
    else if(m<L) update(rt<<1|1,L,R,opt,val);
    else{
        update(rt<<1,L,m,opt,val);
        update(rt<<1|1,m+1,R,opt,val);
    } 
    pushup(rt);
}
int query(int rt,int L,int R,int p){
    if(L==tree[rt].l&&R==tree[rt].r){
        if(p==1) return tree[rt].sum1;
        else if(p==2) return tree[rt].sum2;
        else return tree[rt].sum3;
    }
    pushdown(rt);
    ll ans=0;
    int m=(tree[rt].l+tree[rt].r)>>1;
    if(R<=m) return query(rt<<1,L,R,p); 
    else if(m<L) return query(rt<<1|1,L,R,p);
    else return (query(rt<<1,L,m,p)+query(rt<<1|1,m+1,R,p))%mod;
}
int main(){
    int n,m;
	while(~scanf("%d%d",&n,&m)){
        if(!n&&!m) break;
        build(1,n,1);
        while(m--){
            int opt = read(), x = read(), y = read(), val = read();
            if(opt==4) printf("%d\n",query(1,x,y,val));
            else update(1,x,y,opt,val);
        }
    }
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值