2020牛客多校(第二场) H. Happy Triangle (权值线段树)

Description
Given a multiset M S MS MS and q q q operations. M S MS MS is empty initailly, and operations are in three types, which are as follows:

  1. insert an element x x x into M S MS MS
  2. erase an element x x x from M S MS MS
  3. given an integer x x x, determine whether you can choose two elements a , b a,b a,b in M S MS MS that three segments of length a , b , x a,b,x a,b,x can make a nondegenerate triangle.

Input
The first line contains an integer q   ( 1 ≤ q ≤ 2 × 1 0 5 ) q~(1\leq q\leq 2\times 10^5) q (1q2×105), denoting the number of operations.
Following {q}q lines each contains two integers op, x   ( o p ∈ { 1 , 2 , 3 } , 1 ≤ x ≤ 1 0 9 ) x~(op \in \{1,2,3\}, 1\leq x\leq 10^9) x (op{1,2,3},1x109) denoting an operation
4. if o p = 1 op=1 op=1, insert an element x x x into M S MS MS
5. if o p = 2 op=2 op=2, erase an element x x x from M S MS MS, it’s guaranteed that there is at least one x x x in M S MS MS currently
6. if o p = 3 op=3 op=3, determine whether you can choose two elements a , b a,b a,b in M S MS MS that three segments of length a , b , x a,b,x a,b,x can make a triangle

Output
For each query, print one line containing a string “Yes” if the answer is yes or “No” if the answer is no.

Examples
Input
8
1 1
3 1
1 1
3 2
3 1
1 2
2 1
3 1

Outpu
No
No
Yes
No

Solution
把操作离线,将所有的权值排序并离散化。
用线段树维护区间最值以及区间最小相邻元素差值
操作1和2对应线段树的修改
对于查询操作 x,有三种情况:
① x 作为三角形最小边:若 pos[x] ~ n 的最小差值小于 x 则为 Yes,反之为 No
② x 作为三角形第二大的边:1 ~ pos[x] 的最大元素和pos[x] ~ n 的最小元素分别为另外两条边
③ x 作为三角形最大边:1 ~ pos[x] 的最大元素和次大元素(非严格) 为另外两条边

其他细节见代码

思路来源:@佳爷

Code

#include 
#define ls rt << 1
#define rs rt << 1 | 1
//pair
#define mpp(a,b) make_pair(a,b)
#define fir first 
#define sec second
#define P pair
using namespace std;

typedef long long ll;
const int INF = 2e9;
const int maxn = 2e5+ 5;

int val[maxn];
int num[maxn];
struct Segtree{
    int l,r;
    int mx,mi;
    int by;
}t[maxn << 3];

void push_up(int rt){
    t[rt].mx = max(t[ls].mx,t[rs].mx);
    t[rt].mi = min(t[ls].mi,t[rs].mi);
    t[rt].by = min(t[ls].by,t[rs].by);
    if(t[rs].mi != INF && t[ls].mx != 0) t[rt].by = min(t[rt].by, t[rs].mi - t[ls].mx);
}

void build(int rt,int l,int r){
    t[rt].l = l, t[rt].r = r;
    if(l == r){
        t[rt].mx = 0, t[rt].mi = INF;
        t[rt].by = INF;
        return ;
    }
    int mid = (l + r) >> 1;
    build(ls,l,mid);build(rs,mid+1,r);
    push_up(rt);
}

void update(int rt,int pos,int k){
    int l = t[rt].l, r = t[rt].r;
    if(l == r){
        num[pos] += k;
        // debug1(pos);
        t[rt].by = INF;
        if(num[pos] == 0){
            t[rt].mx = 0; t[rt].mi = INF;
        } else {
            t[rt].mx = t[rt].mi = val[pos];
            if(num[pos] >= 2){
                t[rt].by = 0;
            }
        }
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(ls,pos,k);
    else update(rs,pos,k);
    push_up(rt);
}

int querymx(int rt,int L,int R){
    if(L > R) return 0;
    int l = t[rt].l, r = t[rt].r;
    if(L <= l && r <= R){
        return t[rt].mx;
    }
    int mid = (l + r) >> 1, res = 0;
    if(L <= mid) res = max(res,querymx(ls,L,R));
    if(R  > mid) res = max(res,querymx(rs,L,R));
    return res;
}

int querymi(int rt,int L,int R){
    if(L > R) return INF;
    int l = t[rt].l, r = t[rt].r;
    if(L <= l && r <= R){
        return t[rt].mi;
    }
    int mid = (l + r) >> 1, res = INF;
    if(L <= mid) res = min(res,querymi(ls,L,R));
    if(R  > mid) res = min(res,querymi(rs,L,R));
    return res;
}

int queryby(int rt,int L,int R,int n){
    if(L > R) return INF;
    int l = t[rt].l, r = t[rt].r;
    if(L <= l && r <= R){
        return t[rt].by;
    }
    int mid = (l + r) >> 1, res = INF;
    int f1 = 0, f2 = 0;
    if(L <= mid) {res = min(res,queryby(ls,L,R,n)); f1 = 1;}
    if(R  > mid) {res = min(res,queryby(rs,L,R,n)); f2 = 1;}
    int pos = lower_bound(val + 1,val + 1 + n,t[ls].mx) - val;
    if(f1 && f2 && t[ls].mx != 0 && t[rs].mi != INF && pos >= L) res = min(res,t[rs].mi - t[ls].mx);
    return res;
}

P qu[maxn];

bool check(int n,int pos,int x){
    if(num[pos] >= 2) return true;
    if(num[pos] == 1){
        int la = querymx(1,1,pos-1);if(la != 0 && 1ll * la + x > 1ll * x) return true;
        int minby = queryby(1,pos,n,n);
        if(minby < x) return true;
        return false;
    }
    int lma = querymx(1,1,pos);
    int rmi = querymi(1,pos,n);
    int pos1 = lower_bound(val + 1,val + 1 + n,lma) - val;
    if(lma != 0 && rmi != INF){
        if(1ll * lma + 1ll * x > 1ll * rmi){
            return true;
        }
    }
    if(lma != 0){
        update(1,pos1,-1);
        int lma2 = querymx(1,1,pos);
        update(1,pos1,1);
        if(lma2 != 0 && 1ll * lma + 1ll * lma2 > 1ll * x) return true;
    }
    int by = queryby(1,pos,n,n);
    if(by < x) return true;
    return false;
}

int main(){
    int q;scanf("%d",&q);
    for(int i = 1;i <= q;++i){
        scanf("%d %d",&qu[i].fir,&qu[i].sec);
        val[i] = qu[i].sec;
    }
    sort(val + 1,val + 1 + q);
    int n = unique(val + 1,val + 1 + q) - val - 1;
    build(1,1,n);
    for(int i = 1;i <= q;++i){
        int pos = lower_bound(val + 1,val + 1 + n,qu[i].sec) - val;
        if(qu[i].fir == 1) update(1,pos,1);
        if(qu[i].fir == 2) update(1,pos,-1);
        if(qu[i].fir == 3){
            if(check(n,pos,qu[i].sec)) printf("Yes\n"); else printf("No\n");
        }
    }
    return 0;
}

你可能感兴趣的:(线段树,离散化)