热身小游戏(线段树,单修区修区查)
链接:https://ac.nowcoder.com/acm/contest/37160/G
来源:牛客网
Alice 和 Bob 在玩热身小游戏,Alice 给出 Bob 一个整数 n,n 的初始值为 1,然后给出 q 次操作,每次操作格式如下:
1 a 表示将 n 变为 n×a。
2 l r 表示永久撤销第 l 次到第 r 次操作中的 1 操作。
3 输出 n 模 10^9 + 7 的值。
输入描述:
第一行一个正整数 q ( 1≤q≤105) ,表示操作次数。
第二行至第 q + 1,每行一个第一个正整数 op ( 1≤op≤3 ) 表示此次操作的类型。
如果 op 为 1,则后面有一个整数 a (1≤a≤109);
如果 op 为 2,则后面有两个整数 l ,r ( 1≤l≤r≤105 ) ,并且 r小于当前的操作数;
如果 op 为 3,则当前行结束。请对于每个操作执行对应的操作。
操作下标从 1 开始。
输出描述:
每次进行操作 3 时,输出一行一个数代表值。
示例1
输入
6
1 5
3
1 223
3
2 1 4
3
输出
5
1115
1
思路: 将 q 次操作看成一个长度为 q 的序列,初始值都是 1 。对于第 i 次操作,如果是操作 1 则将下标为 i 的位置修改为 n*a,对于操作 2则进行区间修改为 1,对于操作 3 就是区间求乘积。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lef st,mid,rt<<1
#define rig mid+1,en,rt<<1|1
const int mod=1e9+7,N=5e5+10;
int l,r;
struct pp
{
int l,r,v,lz;
} p[N];
int ksm(int a,int b)
{
int ans=1;
a%=mod;
while(b)
{
if(b%2) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
void pushup(int rt)
{
p[rt].v=p[rt<<1].v*p[rt<<1|1].v%mod;
}
void build(int st,int en,int rt)
{
p[rt]= {st,en,1,0};
if(st==en)
return;
int mid=(st+en)>>1;
build(lef);
build(rig);
pushup(rt);
}
void pushdown(int rt)
{
if(p[rt].lz)
{
p[rt<<1].lz=p[rt].lz;
p[rt<<1|1].lz=p[rt].lz;
p[rt<<1].v=ksm(p[rt].lz,p[rt<<1].r-p[rt<<1].l+1);
p[rt<<1|1].v=ksm(p[rt].lz,p[rt<<1|1].r-p[rt<<1|1].l+1);
p[rt].lz=0;
}
}
void modify(int rt,int val)
{
if(p[rt].l>=l&&p[rt].r<=r)
{
p[rt].lz=val;
p[rt].v=ksm(val,p[rt].r-p[rt].l+1);
}
else
{
pushdown(rt);
int mid=(p[rt].l+p[rt].r)>>1;
if(l<=mid)
modify(rt<<1,val);
if(r>mid)
modify(rt<<1|1,val);
pushup(rt);
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(nullptr);
int n,q,i,j,k;
cin>>q;
build(1,q,1);
for(i=1; i<=q; i++)
{
int op,x;
cin>>op;
if(op==3)
cout<<p[1].v%mod<<"\n";
else if(op==2)
{
cin>>l>>r;
modify(1,1);
}
else
{
cin>>x;
l=r=i;
modify(1,x);
}
}
}