UPC-探险(线段树+二分)

在乏善可陈的世界里,你熠熠生辉。

探险

时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
小林想去森林探险。森林中有一条长度为S的小路:
若位置x有浓雾,则位置x的视野为0;
若从x一直到S或从1一直到x都没有浓雾,则视野为 INF;
其他情况下,位置x的视野定义为max(R-L+1),其中L≤x≤R,∀y∈[L,R],位置y没有浓雾。
具体来说,会有以下事件发生:
1 L R :位置[L,R]产生浓雾;
2 L R :位置[L,R]浓雾消散;
3 x :询问位置x的视野。
一开始,小路上没有任何浓雾。

输入
第一行包含一个整数S。
第二行包含一个整数Q,表示事件总数。
接下来Q行,每行描述一个事件。

输出
对于每个询问事件,输出一行一个整数或字符串”INF”(不含引号)。
样例输入 Copy
5
5
1 2 4
3 1
3 4
2 3 3
3 3
样例输出 Copy
INF
0
1
提示
对于40%的数据,S×Q≤5×10^7。
对于100%的数据,2≤S≤10^5 , 2≤Q≤ 2×10^5,1≤L≤R≤S,1≤x≤S。
思路:
区间修改果断线段树。
这个题查询的地方很巧妙,定义的最大视野是分别向左向右到达的没有浓雾的最远距离。
假设这一点有浓雾,那就输出0;如果没有浓雾,那么在分别向左向右延伸的一段区间肯定也没有浓雾;综上,可以二分分别查找左右的最大边界。
具体细节见代码吧~
代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int, int> PII;
#define I_int ll
#define PI 3.1415926535898
inline ll read()
{
    ll 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;
}
char F[200];
inline void out(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    puts("");
}
const int maxn=1e6+7;
int n,m;
int v,l,r;
struct node{
    int l,r;///左右边界
    int lazy;///懒标记
    int st;//0表示没有浓雾,1表示有浓雾
}a[maxn*4];
void pushup(int u){
    a[u].st=a[u<<1].st|a[u<<1|1].st;//当两个子节点都没都浓雾时才没有浓雾
}
void pushdown(int u){
    auto &root=a[u],&left=a[u<<1],&right=a[u<<1|1];
    if(root.lazy!=-1){
        left.lazy=right.lazy=left.st=right.st=root.lazy;
        root.lazy=-1;
    }
}
void build(int u,int l,int r){
    a[u].l=l,a[u].r=r;
    if(l==r){
        a[u].lazy=a[u].st=0;///初始都没有浓雾
    }
    else{
        int mid=(l+r)>>1;
        build(u<<1,l,mid);build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
void update(int u,int l,int r,int ll,int rr,int v){
    if(l>r||r<ll||l>rr) return ;///完全不包含
    if(l>=ll&&r<=rr){//完全包含
        a[u].st=v;
        a[u].lazy=v;
    }
    else{
        int mid=(l+r)>>1;
        pushdown(u);//传递懒惰标记
        update(u<<1,l,mid,ll,rr,v);//更新左右子树
        update(u<<1|1,mid+1,r,ll,rr,v);
        pushup(u);
    }
}
int query(int u,int l,int r,int ll,int rr){
    if(l>r||r<ll||l>rr) return 0;///完全不包含
    if(l>=ll&&r<=rr) return a[u].st;///完全包含
    int mid=(l+r)>>1;
    pushdown(u);//传递懒惰标记
    int resl=query(u<<1,l,mid,ll,rr),resr=query(u<<1|1,mid+1,r,ll,rr);
    if(rr<=mid) return resl;///完全在左子树
    if(ll>mid) return resr;//完全在右子树
    return (resl|resr);
}
void solve(int x){
    int xx=query(1,1,n,x,x);
    if(xx){//x处有浓雾
        puts("0");
        return ;
    }
    int lx=query(1,1,n,1,x);//从1-x一直没有浓雾
    if(lx==0){
        puts("INF");
        return ;
    }
    int rx=query(1,1,n,x,n);
    if(rx==0){
        puts("INF");
        return ;
    }
    int l=1,r=x,resl=x;
    ///在1-x内 二分找左边的最远视野
    while(l<=r){
        int mid=(l+r)>>1;
        if(query(1,1,n,mid,x)==0){//mid-x一直没浓雾
            resl=mid,r=mid-1;//继续往左找左边界
        }
        else l=mid+1;
    }
    l=x,r=n;int resr=x;
    while(l<=r){
        int mid=(l+r)>>1;
        if(query(1,1,n,x,mid)==0){//x-mid没有浓雾,继续向右寻找右边界
            resr=mid,l=mid+1;
        }
        else r=mid-1;
    }
    out(resr-resl+1);
   // cout<<<<endl;
}
int main(){
    n=read();m=read();
    build(1,1,n);
    while(m--){
        
        v=read();
        if(v==1){
            l=read();r=read();
            update(1,1,n,l,r,1);///产生浓雾
        }
        else if(v==2){
            l=read();r=read();
            update(1,1,n,l,r,0);///消散浓雾
        }
        else{
            int x;x=read();///查询视野
            solve(x);
        }
    }
    return 0;
}

感觉就是个板子题但是写了一个多小时。。
看网上还有用珂朵莉树过的,真·奇奇怪怪的数据结构:传送门

原文地址:https://www.cnblogs.com/OvOq/p/14853177.html