3494. 【NOIP2013模拟联考13】线段(segment) (2017.9B组)

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
想法:
第一次接触动态开点线段树
其实比较简单啦
只不过是如果有需要的话给某个节点赋予它儿子而已
这道题
下传两个lazy标记
先^a,再乘b
下传标记^a*b,至^c *d
c=c*a
d=d^a*b
细节较多
code
线段树最好把多个操作分类,合在一起打会好调很多

#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
using namespace std;
const ll maxn=1e9+7,maxN=2e6,max1=1e9+7;
struct zhj
{
    ll l,r,ans,mi,cheng;    
};
ll q,i,x1,tot,ans,zl,y1,z1;
zhj tree[maxN];
ll ksm(ll x,ll y)
{
    ll k=1,z=x%maxn;
    y%=maxn-1;
    while (y>0)
    {
        if (y&1==1) k=(k*z)%maxn;
        y>>=1;
        z=(z*z)%maxn;   
    }   
    return k;
} 
void xg(ll x,ll l,ll r,ll a,ll b)
{
    tree[x].ans=(ksm(tree[x].ans,a)*ksm(b,r-l+1))%maxn;
    tree[x].mi=(tree[x].mi*a)%(maxn-1);
    tree[x].cheng=(ksm(tree[x].cheng,a)*b)%maxn;
    return;
}
void change(ll x,ll head,ll tail,ll l,ll r,ll zl,ll y)
{
    if ((head==l)&&(tail==r))
    {
        if (zl==1)
        {
            tree[x].ans=(tree[x].ans*ksm(y,tail-head+1))%maxn;
            tree[x].cheng=(tree[x].cheng*y)%maxn;
        }
        if (zl==2)
        {
            tree[x].ans=ksm(tree[x].ans,y);
            tree[x].mi=(tree[x].mi*y)%(maxn-1);
            tree[x].cheng=ksm(tree[x].cheng,y)%maxn;
        }
        if (zl==3)
        {
            ans=(ans*tree[x].ans)%maxn;
        }
        return;
    }
    ll mid=(head+tail)/2,l1,r1;
    if (tree[x].l>0) l1=tree[x].l;
    else tot++,tree[x].l=tot,l1=tot,tree[l1].ans=1,tree[l1].cheng=1,tree[l1].mi=1;

    if (tree[x].r>0) r1=tree[x].r;
    else tot++,tree[x].r=tot,r1=tot,tree[r1].ans=1,tree[r1].cheng=1,tree[r1].mi=1;;

    tree[x].mi%=maxn-1;
    xg(l1,head,mid,tree[x].mi,tree[x].cheng);
    xg(r1,mid+1,tail,tree[x].mi,tree[x].cheng);
    tree[x].mi=1;
    tree[x].cheng=1;
    if (r<=mid) change(l1,head,mid,l,r,zl,y);
    else if (l>mid) change(r1,mid+1,tail,l,r,zl,y);
    else
    {
        change(l1,head,mid,l,mid,zl,y);
        change(r1,mid+1,tail,mid+1,r,zl,y);
    }

    tree[x].ans=(tree[l1].ans*tree[r1].ans)%maxn;
}
int main()
{
    freopen("segment.in","r",stdin);
    freopen("segment.out","w",stdout);
    tot=1;
    tree[1].cheng=1;
    tree[1].mi=1;
    scanf("%lld",&q);
    for (i=1;i<=q;i++)
    {
        scanf("%lld",&zl);
        if (zl==0)
        {
            scanf("%lld%lld%lld",&x1,&y1,&z1);
            y1--;
            change(1,1,max1+max1,x1+max1,y1+max1,1,z1);
        }
        if (zl==1)
        {
            scanf("%lld%lld%lld",&x1,&y1,&z1);
            y1--;
            change(1,1,max1+max1,x1+max1,y1+max1,2,z1%(maxn-1));
        }
        if (zl==2)
        {
            scanf("%lld%lld",&x1,&y1);
            y1--;
            ans=1;
            change(1,1,max1+max1,max1+x1,y1+max1,3,0);
            printf("%lld\n",ans);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值