ZOJ 3998(线段树)

传送门

题面:

Yet Another Data Structure Problem

Time Limit: 5 Seconds      Memory Limit: 65536 KB

Given  integers . There are 3 types of operations:

  • 1 l r v: Multiply  by ;
  • 2 l r k: Change each element in  to ;
  • 3 l r: Query the multiplication of  modulo 1,000,000,007.

For each query, please output the answer.

Input

There are multiple test cases. The first line of the input is an integer  (), indicating the number of test cases. For each test case:

The first line contains two integers  () and  (), indicating the number of the given integers and the number of operations.

The second line contains  integers  (), indicating the given integers.

The first integer on each of the following  lines will be  (), indicating the type of operation.

  • If  equals 1, then three integers  () follow, indicating the first type of operation.
  • If  equals 2, then three integers  () follow, indicating the second type of operation.
  • If  equals 3, then two integers  () follow, indicating an query.

It's guaranteed that neither the sum of  nor the sum of  over all test cases will exceed .

Output

For each query, output one line containing one integer, indicating the answer.

Sample Input

1
5 5
1 2 1 2 1
3 2 4
1 1 5 2
3 2 4
2 1 1 4
3 1 1

Sample Output

4
32
16

题目描述:

    给你一个大小为n的数组,让你进行三种操作。1:将区间l到r的数全都乘上v;2:将区间l到r的数全都变成ai^k;3:求出区间l到r的数的乘积。最后的结果模上1e9+7。

题目分析:

    因为涉及区间更新以及区间求和,因此我们不难想到用线段树去维护区间乘积。

    我们可以发现,第一种操作中,如果更新整个区间[l,r],则等价于在sum[l,r]的基础上再乘上v^(r-l+1);而在第二种操作中,如果更新整个区间[l,r],则等价于对于在区间[l,r]中的每一个a[i],都要把它变成原来的a[i]^k。我们可以发现,两种操作中,第一种操作时只跟底数v有关的,第二种操作是只与质数k有关的,因此我们可以想到将底数和指数分别作为两个lazy标记分别去维护两种更新操作。

    假设a[i]是底数的lazy标记,x[i]是指数的lazy标记。那么倘若a[i]需要进行push_down操作,我们只需要将a[i]的值像类似区间和更新的操作一样push_down即可,同时再将该区间的区间积乘上pow(a[i],size)即可。

    而倘若要对x[i]进行push_down操作,首先我们要知道,因为最后的结果要取模,而根据费马小定理,  ,我们知道每次对指数x[i]%(mod-1)更新即可。而对于a[i]以及结点的值变为对应的x[i]次即可。

    ps:这个题时间卡的很紧,必须得用读入优化。

#include <bits/stdc++.h>
#define maxn 2000005
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll sum[maxn];
ll a[maxn];
ll x[maxn];
ll read()
{
	ll ret=0;
	char ch=getchar();
	while(ch>'9'||ch<'0')ch=getchar();
	while(ch>='0'&&ch<='9')
	{
		ret=ret*10+ch-'0';
		ch=getchar();
	}
	return ret;
}
ll powmod(ll a,ll b){
    int res=1;
    while(b){
        if(b&1) res=1ll*a*res%mod;
        b>>=1;
        a=1ll*a*a%mod;
    }
    return res;
}
void push_up(int rt){
    sum[rt]=1ll*sum[rt<<1]*sum[rt<<1|1]%mod;
}
void build(int l,int r,int rt){
    a[rt]=x[rt]=1;
    if(l==r){
        sum[rt]=read();
        //scanf("%lld",&sum[rt]);
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    push_up(rt);
}
void push_down(int rt,int ln,int rn){
    if(x[rt]>1){//若指数能进行push_down
        a[rt<<1]=powmod(a[rt<<1],x[rt]);
        a[rt<<1|1]=powmod(a[rt<<1|1],x[rt]);//更新底数的值
        
        x[rt<<1]=1ll*x[rt<<1]*x[rt]%(mod-1);//更新指数的值(费马小定理)
        x[rt<<1|1]=1ll*x[rt<<1|1]*x[rt]%(mod-1);
        
        sum[rt<<1]=powmod(sum[rt<<1],x[rt]);//更新结点的值
        sum[rt<<1|1]=powmod(sum[rt<<1|1],x[rt]);
        x[rt]=1;
    }
    if(a[rt]>1){//若底数能进行push_down
        a[rt<<1]=1ll*a[rt<<1]*a[rt]%mod;
        a[rt<<1|1]=1ll*a[rt<<1|1]*a[rt]%mod;//更新底数
        
        sum[rt<<1]=1ll*sum[rt<<1]*powmod(a[rt],ln)%mod;//更新结点的值
        sum[rt<<1|1]=1ll*sum[rt<<1|1]*powmod(a[rt],rn)%mod;
        a[rt]=1;
    }
}
void update1(int L,int R,int l,int r,int rt,int v){//区间更新1,对区间[L,R]都乘上v
    if(L<=l&&R>=r){
        a[rt]=1ll*a[rt]*v%mod;
        sum[rt]=1ll*sum[rt]*powmod(v,(r-l+1))%mod;
        return;
    }
    int mid=(l+r)>>1;
    push_down(rt,mid-l+1,r-mid);
    if(L<=mid) update1(L,R,l,mid,rt<<1,v);
    if(R>mid) update1(L,R,mid+1,r,rt<<1|1,v);
    push_up(rt);
}
void update2(int L,int R,int l,int r,int rt,int k){//区间更新2,使区间[L,R]的值都变为原来的a[i]^k
    if(L<=l&&R>=r){
        a[rt]=1ll*powmod(a[rt],k);
        x[rt]=1ll*x[rt]*k%(mod-1);
        sum[rt]=powmod(sum[rt],k);
        return;
    }
    int mid=(l+r)>>1;
    push_down(rt,mid-l+1,r-mid);
    if(L<=mid) update2(L,R,l,mid,rt<<1,k);
    if(R>mid) update2(L,R,mid+1,r,rt<<1|1,k);
    push_up(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        return sum[rt];
    }
    int mid=(l+r)>>1;
    push_down(rt,mid-l+1,r-mid);
    ll res=1ll;
    if(L<=mid) res=1ll*res*query(L,R,l,mid,rt<<1)%mod;
    if(R>mid) res=1ll*res*query(L,R,mid+1,r,rt<<1|1)%mod;
    return res;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        n=read();
        m=read();
        //scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--){
            int op,l,r;
            op=read();
            l=read();
            r=read();
            //scanf("%d%d%d",&op,&l,&r);
            if(op==1){
                int v;
                v=read();
                //scanf("%d",&v);
                update1(l,r,1,n,1,v);
            }
            else if(op==2){
                int k;
                k=read();
                //scanf("%d",&k);
                update2(l,r,1,n,1,k);
            }
            else{
                ll res=query(l,r,1,n,1);
                printf("%lld\n",res);
            }
        }
    }
    return 0;
}

posted @ 2018-06-10 15:39  ChenJr  阅读(225)  评论(0编辑  收藏  举报