K - Transformation (区间标记思想)

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

题解:题意很好懂吧,就不赘述了;这个题主要用到了标记思想,否则会很麻烦的,而且还不一定能做出来~~

第一次做这样的题是死活也想不出来这样的思想。看到大神的博客后恍然大悟:

https://blog.csdn.net/qq_36386435/article/details/83280634

我们建立线段树的同时开个vis标记数组,如果该节点为1说明其所有子节点的值全部相同,否则不同。那么到查询的时候我们对于节点标记为1的并且在查询区间内的则可大大简化,由于该节点的所有子节点的值全部相同,则只需算出来一个的p次方,再乘以区间长度即可~~~其他的看代码的注释吧~~

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
const int mod=10007;
int n,m,x,y,c,q;
int tree[maxn<<2],vis[maxn<<2];//vis为标记数组,如果为1,说明此节点的所有子节点的值全部一样,否则不一样
void pushdown(int rt)//自上往下更新
{
    if(vis[rt])
    {
        vis[rt*2]=vis[rt*2+1]=1;//如果根节点标记为1,那么子节点一定也是标记为1
        vis[rt]=0;//这时取消对根节点的标记即可
        tree[rt*2]=tree[rt*2+1]=tree[rt];//根节点标记为1说明子节点的值全部一样,为左右孩子赋值
    }
}
void update(int q,int L,int R,int c,int l,int r,int rt)
{
    if(L<=l&&R>=r&&vis[rt])//如果该ĵ区间在查询区间范围内并且该根节点标记为1说明它以下的所有子节点值全部一样,对此根节点进行操作,之后pushdown即可
    {
        if(q==1)           //主函数里没有分类,这里分1到3类
            tree[rt]=(tree[rt]+c)%mod;
        else if(q==2)
            tree[rt]=(tree[rt]*c)%mod;
        else if(q==3)
            tree[rt]=c;
        return ;
    }
    pushdown(rt);   //下放
    int mid=(l+r)/2;
    if(L<=mid)  //左右区间就不多说了
        update(q,L,R,c,l,mid,rt*2);
    if(R>=mid+1)
        update(q,L,R,c,mid+1,r,rt*2+1);
    //进行一波更新以后,其对应的值已经发生改变,所以每次都得更新vis数组,而pushdown只是简简单单的下放
    if(!vis[rt*2]||!vis[rt*2+1])//如果子节点有假,那么该根节点必为假
        vis[rt]=0;
    else     //子节点全部标记为真
    {
        if(tree[rt*2]!=tree[rt*2+1])//如果左右孩子的值不一样,那么这个标记不能做到该根节点上去
            vis[rt]=0;
        else//如果能做到,就将该根节点标记为1,同时为根节点赋值
        {
            vis[rt]=1;
            tree[rt]=tree[rt*2];
        }
    }
}
int querysum(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l&&R>=r&&vis[rt])//如果该区间在查找区间范围内,并且该根节点标记为1说明他下面的子节点全部值都一样,可简化如下
    {
        int ans=1;
        for(int i=1;i<=c;i++)
        {
            ans=ans*tree[rt]%mod;//求一个数的p次方
        }
        return ans*(r-l+1)%mod;//区间里有r-l+1个一样的值,所以只要求出来1个再乘以区间长度即可
    }
    pushdown(rt);
    int ans=0;
    int mid=(l+r)/2;
    if(L<=mid)          //左右递归查询就不多说了
        ans+=querysum(L,R,c,l,mid,rt*2);
    if(R>=mid+1)
        ans+=querysum(L,R,c,mid+1,r,rt*2+1);
    return ans%mod;
}
int main()
{
    while(cin>>n>>m)
    {
        if(n+m==0)
            break;
        memset(tree,0,sizeof(tree));//由于初始值全为0,那么直接清零即可,不用写建树函数了
        memset(vis,1,sizeof(vis));//初始化为1,说明所有节点的所有子节点全部相同(即全为0)
        for(int i=1;i<=m;i++)
        {
            cin>>q>>x>>y>>c;
            if(q<=3)
                update(q,x,y,c,1,n,1);//1大类更新,根据自己的喜好来选定参数值的表达方式即可
            else
            {
                int ans=querysum(x,y,c,1,n,1);
                cout<<ans<<endl;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824158/article/details/88648698