题目描述
Description
数轴上有很多单位线段,一开始时所有单位线段的权值都是1。有两种操作,第一种操作将某一区间内的单位线段权值乘以w,第二种操作将某一区间内的单位线段权值取w次幂。并且你还需要回答一些询问,每个询问需要求出某一区间的单位线段权值之积。由于答案可能很大,你只需要求出答案 mod (10^9+7)的值。
说明:n个点只有n-1条线段。
Input
第一行一个整数n,表示操作数量。
接下来n行,每行第一个整数表示操作类型,0表示第一种操作,1表示第二种操作,2表示询问,如果第一个数是0或1,则接下来3个数,表示操作区间和w,否则接下来两个数,表示询问区间。
Output
对于每组询问,输出一行,表示所求答案。
Sample Input
7
0 0 2 3
1 1 3 2
2 1 3
0 0 3 2
1 1 3 2
2 1 3
2 0 3
Sample Output
9
1296
7776
分析
如果数据范围较小的话,可以直接用线段树搞。
维护每段区间幂的次数和乘的数,根据费马小定理,幂可以与
109+6
取模。
动态开点线段树
如果按照数据范围来开点,区间长度过大的情况下会炸。
但是询问的次数很少,所以其实很多节点都是无用的。
所以可以根据与操作有关的区间动态开点。
code
#include <iostream>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 1000000007
#define pmod 1000000006
#define mx 2000000001
using namespace std;
int n,len,x,y,z,i,s;
long long tr[4000000][6];
long long ans;
//0leftson 1rightson 2data 3multiplicative 4power 5length
void newt(int t,int s)//动态开点
{
len++;
tr[t][s]=len;
tr[len][2]=1;
tr[len][3]=1;
tr[len][4]=1;
tr[len][5]=tr[t][5]>>1;
if ((tr[t][5]&1) && (!s))
tr[len][5]++;
}
long long qpower(long long a,int b)
{
long long ans=1;
a%=mod;
while (b)
{
if (b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
void down(int t)//下传标记
{
if (!tr[t][0])
newt(t,0);
if (!tr[t][1])
newt(t,1);
if (tr[t][4]!=1)
{
tr[tr[t][0]][4]=(tr[tr[t][0]][4]*tr[t][4])%pmod;
tr[tr[t][0]][3]=qpower(tr[tr[t][0]][3],tr[t][4]);
tr[tr[t][1]][4]=(tr[tr[t][1]][4]*tr[t][4])%pmod;
tr[tr[t][1]][3]=qpower(tr[tr[t][1]][3],tr[t][4]);
tr[t][2]=qpower(tr[t][2],tr[t][4]);
tr[t][4]=1;
}
if (tr[t][3]!=1)
{
tr[tr[t][0]][3]=(tr[tr[t][0]][3]*tr[t][3])%mod;
tr[tr[t][1]][3]=(tr[tr[t][1]][3]*tr[t][3])%mod;
tr[t][2]=(tr[t][2]*qpower(tr[t][3],tr[t][5]))%mod;
tr[t][3]=1;
}
}
void up(int t)
{
tr[t][2]=(tr[tr[t][0]][2]*tr[tr[t][1]][2])%mod;
}
void change(int t,long long l,long long r,int x,int y,int s1,int s2)
{
if (!tr[t][0])
newt(t,0);
if (!tr[t][1])
newt(t,1);
down(tr[t][0]);
down(tr[t][1]);
if ((x<=l) && (r<=y))
{
tr[t][3]=s1;
tr[t][4]=s2;
down(t);
return;
}
long long mid=(l+r)/2;
if (x<=mid)
change(tr[t][0],l,mid,x,y,s1,s2);
if (mid<y)
change(tr[t][1],mid+1,r,x,y,s1,s2);
up(t);
}
void find(int t,long long l,long long r,int x,int y)
{
if (!tr[t][0])
newt(t,0);
if (!tr[t][1])
newt(t,1);
down(tr[t][0]);
down(tr[t][1]);
if ((x<=l) && (r<=y))
{
ans=(ans*tr[t][2])%mod;
return;
}
long long mid=(l+r)/2;
if (x<=mid)
find(tr[t][0],l,mid,x,y);
if (mid<y)
find(tr[t][1],mid+1,r,x,y);
up(t);
}
int main()
{
freopen("segment.in","r",stdin);
freopen("segment.out","w",stdout);
scanf("%d",&n);
len=1;
tr[1][2]=1;
tr[1][3]=1;
tr[1][4]=1;
tr[1][5]=mx;
fo(i,1,n)
{
scanf("%d%d%d",&s,&x,&y);
if (s<2)
scanf("%d",&z);
x+=1000000001;//避免区间范围为负
y+=1000000000;
if (s==0)
change(1,1,mx,x,y,z,1);
else
if (s==1)
change(1,1,mx,x,y,1,z);
else
{
ans=1;
find(1,1,mx,x,y);
printf("%d\n",ans);
}
}
}