Transformation

版权声明:来自星空计算机团队——申屠志刚 https://blog.csdn.net/weixin_43272781/article/details/83549838

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

C++版本一

//长度为n的数组 四个操作
//1 x y  c [x,y]区间的数都加c
//2 x y c [x, y] 区间的数都乘以c
//3 x y c [x ,y] 区间的数都变为c
//4 x y p [x ,y] 求区间的数的p次方的和
//用线段树维护里面的值都相等的区间 ,那么这个区间的所需答案为(r-l+1)*(tree[v].eq)^q
//对于懒惰操作 mul , eq , add的处理是
//对于每次eq操作可以直接操作tree[v].eq = eq ;tree[v].mul = 1 ; tree[v].add = 0;
//而对于mul ,和add的操作每次push_down(v)的时候保证下面一层的所有懒惰情况都是初始情况
//所以对于mul和add操作时就一直往下push_down知道找到一个可以
 

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 100010 ;
#define left v<<1
#define right v<<1|1
#define mod  10007
struct node
{
    int l ,r , value ;
    int eq , add , mul ;
}tree[maxn<<2];
void build(int l , int r  , int v)
{
    tree[v].l = l ;
    tree[v].r = r ;
    tree[v].add = 0 ; tree[v].mul = 1 ;tree[v].eq = -1 ;
    if(l == r)
    {tree[v].eq = 0 ; return  ;}
    int mid = (l + r) >> 1 ;
    build(l , mid , left) ;
    build(mid + 1 , r , right) ;
}
void push_down(int v)
{
    if(tree[v].l == tree[v].r)return ;
    if(tree[v].eq != -1)
    {
        tree[left].eq = tree[right].eq = tree[v].eq ;
        tree[left].add = tree[right].add = 0 ;
        tree[left].mul = tree[right].mul = 1;
        tree[v].eq = -1;
        return  ;
    }
    if(tree[v].mul != 1)
    {
        if(tree[left].eq != -1)
        tree[left].eq = (tree[left].eq*tree[v].mul)%mod ;
        else
        {
            push_down(left) ;
            tree[left].mul = (tree[left].mul*tree[v].mul)%mod ;
        }
        if(tree[right].eq != -1)
        tree[right].eq = (tree[right].eq*tree[v].mul)%mod ;
        else
        {
            push_down(right) ;
            tree[right].mul = (tree[right].mul*tree[v].mul)%mod ;
        }
        tree[v].mul = 1;
    }
    if(tree[v].add)
    {
        if(tree[left].eq != -1)
        tree[left].eq = (tree[left].eq + tree[v].add)%mod ;
        else
        {
            push_down(left) ;
            tree[left].add = (tree[left].add + tree[v].add)%mod ;
        }
        if(tree[right].eq != -1)
        tree[right].eq = (tree[right].eq + tree[v].add)%mod ;
        else
        {
            push_down(right) ;
            tree[right].add = (tree[right].add + tree[v].add)%mod ;
        }
        tree[v].add = 0 ;
    }
}
void update(int l , int r , int v , int op , int c)
{
    if(l <= tree[v].l && tree[v].r <= r)
    {
        if(op == 3)
        {
            tree[v].add = 0 ;tree[v].mul = 1;
            tree[v].eq  = c ;
            return ;
        }
        if(tree[v].eq != -1)
        {
            if(op == 1)tree[v].eq = (tree[v].eq + c)%mod ;
            else tree[v].eq = (tree[v].eq*c)%mod ;
        }
        else
        {
            push_down(v) ;
            if(op == 1)tree[v].add = (tree[v].add + c)%mod ;
            else tree[v].mul = (tree[v].mul*c)%mod ;
        }
        return ;
    }
    push_down(v) ;
    int mid = (tree[v].l + tree[v].r) >> 1 ;
    if(l <= mid)update(l , r ,left , op , c) ;
    if(r > mid)update(l , r , right , op , c) ;
}
int query(int l , int r , int v , int q)
{
    if(tree[v].l >= l && tree[v].r <= r && tree[v].eq != -1)
    {
        int ans = 1;
        for(int i = 1;i <= q;i++)
        ans = (ans * tree[v].eq)%mod ;
        return (ans*((tree[v].r - tree[v].l + 1)%mod))%mod ;
    }
    push_down(v) ;
    int mid = (tree[v].l  + tree[v].r) >> 1 ;
    if(l > mid)return query(l , r , right, q) ;
    else if(r <= mid)return query(l , r ,left ,q) ;
    else return (query(l , mid , left , q) + query(mid + 1 , r , right , q))%mod ;
}
int main()
{
    //freopen("in.txt" ,"r" , stdin) ;
    int n , m ;
    while(scanf("%d%d" , &n , &m) &&(n+m))
    {
        int op , x ,  y , c;
        build(1 , n , 1) ;
        while(m--)
        {
            scanf("%d%d%d%d" , &op , &x , &y , &c) ;
            if(op == 4)
            printf("%d\n" , (query(x, y , 1 , c)%mod)) ;
            else update(x , y , 1 , op , c) ;
        }
    }
    return  0 ;
}

C++版本二

题解:

我只能说这题很迷,迷之WA,这题我陆陆续续做了4天才AC,其中WA了3页,共计40多次吧,重写代码3次,用各种不同方法写。。最后无奈看着别人的博客照着一个大佬的写就AC了。。吐血,一开始我打算用3个v,3个tag来写,3个v表示区间的一二三次方值,tag分别表示加,乘等于的标记,先处理等,如果有等号标记,区间更新,清除加号和乘号标记,在处理乘和加之前看子区间有没有等号标记,有就先处理子区间的等号标记,然后处理乘,如果同时有加号标记把加的值乘上一个要乘的数,最后处理乘号标记,至于一次方二次方三次方公式是搞数学的老刘推的。。结果就是我样例过了,自己测的几组也过了还是WA,各种迷,怎么修改都是WA,后来看到一个大佬的没有推公式也没有储存三次方。。方法很神奇,而且速度比一般用储存3个值的那种快得多。。

说一下思路:

同样tag1表示加号标记,tag2表示乘,tag3表示等,无疑在向下更新的时候等号的优先级应该最高,先处理等号标记,并把加号和乘号标记置为0和1(初始值),然后重点来了,在处理乘号标记的时候,如果发现子区间有等号标记,直接修改等号标记,否则先将子区间pushdwon一下清空标记,再进行该子区间标记,加号同理,如果子区间有等号标记,就修改等号标记,否则将子区间pushdown清空标记,再将该子区间标记,所有操作完都要清空当前区间标记,然后修改的时候也差不多,先检查该区间有没有等号标记,有的话直接修改等号标记,否则pushdown清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。。。。一开始我还以为会超时,结果居然比一般的算法快得多,膜拜大佬
 

#include<algorithm>
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<string>
#include<stdio.h>
#include<queue>
#include<stack>
#include<map>
#include<deque>
using namespace std;
#define left k*2
#define right k*2+1
const int N=10007;
struct node
{
    int l,r;
    int tag1,tag2,tag3;//分别表示加号,乘号,等号标记
}t[100005*4];
int n;
void Build(int l,int r,int k)
{
    t[k].l=l;
    t[k].r=r;
    t[k].tag1=0;
    t[k].tag2=1;
    t[k].tag3=-1;
    if(l==r)
    {
        t[k].tag3=0;//最底层要赋值为0
        return;
    };
    int mid=(l+r)/2;
    Build(l,mid,left);
    Build(mid+1,r,right);
}
void pushdown(int k)
{
    if(t[k].l==t[k].r)//没有子区间了不用退了
        return;
    if(t[k].tag3!=-1)//处理等号
    {
        t[left].tag3=t[right].tag3=t[k].tag3;//更新子区间等号标记
        t[left].tag2=t[right].tag2=1;
        t[left].tag1=t[right].tag1=0;//清空子区间加乘标记
        t[k].tag3=-1;
        return;
    }
    if(t[k].tag2!=1)//处理乘号
    {
        if(t[left].tag3!=-1)//如果子区间有等号标记,直接修改等号标记
            t[left].tag3=(t[left].tag3*t[k].tag2)%N;
        else//否则清空该子区间标记,进行子区间标记
        {
            pushdown(left);
            t[left].tag2=(t[left].tag2*t[k].tag2)%N;
        }
        if(t[right].tag3!=-1)//同理处理右区间
            t[right].tag3=(t[right].tag3*t[k].tag2)%N;
        else
        {
            pushdown(right);
            t[right].tag2=(t[right].tag2*t[k].tag2)%N;
        }
        t[k].tag2=1;
    }
    if(t[k].tag1!=0)//处理加号标记,和上面同理处理
    {
        if(t[left].tag3!=-1)
            t[left].tag3=(t[left].tag3+t[k].tag1)%N;
        else
        {
            pushdown(left);
            t[left].tag1=(t[left].tag1+t[k].tag1)%N;
        }
        if(t[right].tag3!=-1)
            t[right].tag3=(t[right].tag3+t[k].tag1)%N;
        else
        {
            pushdown(right);
            t[right].tag1=(t[right].tag1+t[k].tag1)%N;
        }
        t[k].tag1=0;//记得还原
    }
}
void update(int l,int r,int v,int d,int k)
{
    if(t[k].l==l&&t[k].r==r)
    {
        if(d==1)
        {
            if(t[k].tag3!=-1)//如果有等号标记,就直接修改等号标记
            {
                t[k].tag3=(t[k].tag3+v%N)%N;
            }
            else
            {
                pushdown(k);//否则清空该区间,进行标记
                t[k].tag1=(t[k].tag1+v%N)%N;
            }
        }
        else if(d==2)//同理
        {
            if(t[k].tag3!=-1)
            {
                t[k].tag3=(t[k].tag3*v%N)%N;
            }
            else
            {
                pushdown(k);
                t[k].tag2=(t[k].tag2*v%N)%N;
            }
        }
        else
        {
            t[k].tag3=v%N;
            t[k].tag1=0;
            t[k].tag2=1;
        }
        return;
    }
    pushdown(k);//向下更新
    int mid=(t[k].l+t[k].r)/2;
    if(r<=mid)
        update(l,r,v,d,left);
    else if(l>mid)
        update(l,r,v,d,right);
    else
    {
        update(l,mid,v,d,left);
        update(mid+1,r,v,d,right);
    }
}
int query(int l,int r,int p,int k)//查询
{
    if(t[k].l>=l&&t[k].r<=r&&t[k].tag3!=-1)//查到是查询区间的子区间且一段全为相同的数
    {
        int temp=1;
        for(int i=1;i<=p;i++)
            temp=(temp*t[k].tag3)%N;
        return ((t[k].r-t[k].l+1)%N*temp)%N;//注意要乘上长度
    }
    pushdown(k);
    int mid=(t[k].l+t[k].r)/2;
    if(r<=mid)
        return query(l,r,p,left)%N;
    else if(l>mid)
        return query(l,r,p,right)%N;
    else
    {
        return (query(l,mid,p,left)+query(mid+1,r,p,right))%N;
    }
}
int main()
{
    int i,j,k,m,d,x,y,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        Build(1,n,1);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d%d",&d,&x,&y,&c);
            if(d>=1&&d<=3)
            {
                update(x,y,c,d,1);
            }
            else
            {
                printf("%d\n",query(x,y,c,1)%N);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43272781/article/details/83549838