[JZOJ4610]线段树

题目大意

这里写图片描述
这里写图片描述

解题思路

先考虑询问怎么做。
一个很关键的想法是,我们可以把操作了[l,r]的 ak a k 表示成一段区间的最大值,如果知道了这个区间,问题就很简单了。假设为max{a[le..ri]}
假设有一个假如我们有一个(l,r,k),我们怎么知道le和ri呢?
注意描述中带下标的l,r和不带下标的不是同一个东西。
我们从r开始,找到尽量晚的,能够覆盖k的区间,假设为 [li,ri] [ l i , r i ] ,然后我们再找尽量大的j,使得 li[lj,rj],j<i l i ∈ [ l j , r j ] , j < i 。同样对 ri r i 找一个j’,然后再用j找到类似的 j2j2 j 2 和 j 2 ′ ,直到小于询问的l为止。当然小于的那个不要,左右两边扩展的次数也不一定一样。那么就找到了le和ri。
考虑优化这一过程,弄一颗“左树”一颗“右树”,一个点代表某个区间。左树中,一个点i的父亲指向j。(类似于上面的定义)右树类似。那么我们可以倍增找。
怎么找到尽量大的j?线段树区间打标记,单点查询。
那么你把询问离线挂在各自的r上面就好了。预处理出le和ri
剩下的问题就是区间最大值查询和单点修改了。
时间复杂度O(nlogn)

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)*
const int N=1e5+5,M=4e5+5,mo=1e9+7,rt=3;
struct rec
{
    int ka,x,y,z;
}qu[N];
int tr[M],tag[M],d[N],n,m,q,x,y,z,le[N],ri[N],Le[N],Ri[N],Log[N],i,a[N],l,lf[20][N],rf[20][N],j;
bool cmp(int a,int b)
{
    return qu[a].y<qu[b].y;
}
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') 
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void down(int x,int bot)
{
    if (!tag[x]) return ;
    tr[x]=tag[x];
    if (!bot) tag[x*2]=tag[x*2+1]=tag[x];
    tag[x]=0;
}
void change(int x,int l,int r,int i,int j,int v)
{
    if (l==i&&r==j)
    {
        tag[x]=v;
        down(x,l==r);
        return ;
    }
    int m=l+r>>1;
    down(x*2,l==m);
    down(x*2+1,m+1==r);
    if (j<=m) change(x*2,l,m,i,j,v);
    else if (m<i)  change(x*2+1,m+1,r,i,j,v);
    else change(x*2,l,m,i,m,v),change(x*2+1,m+1,r,m+1,j,v);
    tr[x]=max(tr[x*2],tr[x*2+1]);
}
int get(int x,int l,int r,int i,int j)
{
    if (l==i&&r==j) return tr[x];
    int m=l+r>>1;
    down(x*2,l==m);
    down(x*2+1,m+1==r);
    if (j<=m) return get(x*2,l,m,i,j);
    else if (m<i) return get(x*2+1,m+1,r,i,j);
    else return max(get(x*2,l,m,i,m),get(x*2+1,m+1,r,m+1,j));
}
void clear(int x,int l,int r)
{
    tr[x]=tag[x]=0;
    if (l==r) 
    {
        tr[x]=a[l];
        return ;
    }
    int m=l+r>>1;
    clear(x*2,l,m);
    clear(x*2+1,m+1,r);
    tr[x]=max(tr[x*2],tr[x*2+1]);
}
int main()
{
    freopen("t7.in","r",stdin);
    freopen("t7.out","w",stdout);
    n=read();m=read();q=read();
    fo(i,1,n) a[i]=read();
    fo(i,1,m)
    {
        le[i]=read();
        ri[i]=read();
    }
    fo(i,1,q)
    {
        qu[i].ka=read();
        qu[i].x=read();
        qu[i].y=read();
        if (qu[i].ka==2) qu[i].z=read(),d[++d[0]]=i;
    }
    sort(d+1,d+1+d[0],cmp);
    l=1;
    fo(i,1,m) Log[i]=trunc(log(i)/log(2));
    fo(i,1,m)
    {
        lf[0][i]=get(1,1,n,le[i],le[i]);
        rf[0][i]=get(1,1,n,ri[i],ri[i]);
        fo(j,1,Log[m]) lf[j][i]=lf[j-1][lf[j-1][i]],rf[j][i]=rf[j-1][rf[j-1][i]];
        change(1,1,n,le[i],ri[i],i);
        while (qu[d[l]].y==i)
        {
            x=d[l];
            z=get(1,1,n,qu[x].z,qu[x].z);
            if (z<qu[x].x) z=0;
            y=z;
            fd(j,Log[m],0) if (lf[j][y]>=qu[x].x) y=lf[j][y];
            Le[x]=le[y];
            y=z;
            fd(j,Log[m],0) if (rf[j][y]>=qu[x].x) y=rf[j][y];
            Ri[x]=ri[y];
            if (!z) Le[x]=Ri[x]=qu[x].z;
            l++;
        }
    }
    clear(1,1,n);
    fo(i,1,q)
        if (qu[i].ka==1)
            change(1,1,n,qu[i].x,qu[i].x,qu[i].y);
        else 
        {
            printf("%d\n",get(1,1,n,Le[i],Ri[i]));
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值