题目链接

题面:

题意:
给定一个多重集合,有以下三种操作。
1。往集合里面插入一个数x。
2。从集合里面删除数x。
3。给定x询问集合中是否有两个数a,b,使得a,b,x三个数能组成一个非退化的三角形。(就是正常的三角形)。

官方题解:

题解:
考虑每个询问 x。
我们设有其他两边 a<=b
若x是最长的边,那么需要a+b>x,我们可以选择小于x的最大的和次大的数来判断。
若x不是最长边,那么需要a+x>b,转化一下,x>b-a 我们可以找最小的 b-a 来判断。

我们需要找小于x的最大的和次大的数,维护一个最小的b-a。
找小于x的最大的数和次大的数,是平衡树的基本操作。
维护b-a时,我们可以维护一个子树中的最小差值,子树最大值和子树最小值,然后转移即可。

其实这个玩意,感觉动态开点线段树也可?

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
//#define lc (cnt<<1)
//#define rc (cnt<<1|1)
#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=2e9+10;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxm=100100;
const int up=100000;
#define int ll
struct node
{
    int val;
    int lc,rc;
    int si,cnt;
    int minn,mi,ma;
}t[maxn];
int tot,root;



void pushup(int p)
{
    t[p].si=t[t[p].lc].si+t[t[p].rc].si+1;
    t[p].mi=min(t[p].val,min(t[t[p].lc].mi,t[t[p].rc].mi));
    t[p].ma=max(t[p].val,max(t[t[p].lc].ma,t[t[p].rc].ma));
    t[p].minn=min(t[t[p].lc].minn,t[t[p].rc].minn);
    //这里注意处理,因为0号节点mi设置为了inf,ma设置为了-inf,t[t[p].rc].mi-t[p].val ,t[p].val-t[t[p].lc].ma就有可能为0了。
    t[p].minn=min(t[p].minn,min(t[p].rc==0?inf:t[t[p].rc].mi-t[p].val,t[p].lc==0?inf:t[p].val-t[t[p].lc].ma));
    t[p].minn=(t[p].cnt>=2?0:t[p].minn);
}


int newnode(int x)
{
    int p=++tot;
    t[p].lc=t[p].rc=0;
    t[p].cnt=t[p].si=1;
    t[p].val=t[p].mi=t[p].ma=x;
    t[p].minn=inf;
    return p;
}

void R(int &p)
{
    int q=t[p].lc;
    t[p].lc=t[q].rc;
    t[q].rc=p;
    p=q;
    pushup(t[p].rc);
    pushup(p);

}

void L(int &p)
{
    int q=t[p].rc;
    t[p].rc=t[q].lc;
    t[q].lc=p;
    p=q;
    pushup(t[p].lc);
    pushup(p);
}

void RL(int &p)
{
    R(t[p].rc);
    L(p);
}

void LR(int &p)
{
    L(t[p].lc);
    R(p);
}


int getmin(int p)
{
    while(t[p].lc) p=t[p].lc;
    return p;
}

int getmax(int p)
{
    while(t[p].rc) p=t[p].rc;
    return p;
}

void maintain(int &p,bool flag)
{
    if(!flag)
    {
        if(t[t[t[p].lc].lc].si>t[t[p].rc].si)
            R(p);
        else if(t[t[t[p].lc].rc].si>t[t[p].rc].si)
            LR(p);
        else return ;
    }
    else
    {
        if(t[t[t[p].rc].rc].si>t[t[p].lc].si)
            L(p);
        else if(t[t[t[p].rc].lc].si>t[t[p].lc].si)
            RL(p);
        else return ;
    }
    maintain(t[p].lc,false);
    maintain(t[p].rc,true);
    maintain(p,true);
    maintain(p,false);
}

void _insert(int &now,int val)
{
    if(!now)
    {
        now=newnode(val);
        return ;
    }
    if(val<t[now].val)
    {
        _insert(t[now].lc,val);
        maintain(now,false);
    }
    else if(val>t[now].val)
    {
        _insert(t[now].rc,val);
        maintain(now,true);
    }
    else
    {
        t[now].cnt++;
    }
    pushup(now);
}

void del(int &p,int x)
{
    if(x<t[p].val)
        del(t[p].lc,x);
    else if(x>t[p].val)
        del(t[p].rc,x);
    else
    {
        if(t[p].cnt>1)
        {
            t[p].cnt--;

        }
        else
        {
             if(t[p].lc&&t[p].rc)
            {
                int q=getmax(t[p].lc);
                t[p].val=t[q].val;
                t[p].cnt=t[q].cnt;
                t[q].cnt=1;
                del(t[p].lc,t[q].val);
            }
            else
            {
                if(t[p].lc) p=t[p].lc;
                else p=t[p].rc;
                return ;
            }
        }
    }
    pushup(p);
}


int get_rank(int x)
{
    int now=root,ans=0;
    while(now)
    {
        if(t[now].val<x) ans+=t[t[now].lc].si+1,now=t[now].rc;
        else now=t[now].lc;
    }
    return ans+1;
}

int get_val(int k)
{
    int now=root;
    while(1)
    {
        if(t[t[now].lc].si+1==k) return t[now].val;
        else if(t[t[now].lc].si>=k) now=t[now].lc;
        else k-=t[t[now].lc].si+1,now=t[now].rc;
    }
}

int get_front(int x)
{
    int now=root,ans=-inf;
    while(now)
    {
        if(t[now].val<x) ans=max(ans,t[now].val),now=t[now].rc;
        else now=t[now].lc;
    }
    return ans;
}

int get_behind(int x)
{
    int now=root,ans=inf;
    while(now)
    {
        if(t[now].val>x) ans=min(ans,t[now].val),now=t[now].lc;
        else now=t[now].rc;
    }
    return ans;
}

int find_val(int x)
{
    int now=root,ans=inf;
    int maxx=-inf;
    while(now)
    {
        if(t[now].val>=x)
        {
            ans=min(ans,t[now].cnt>=2?0ll:inf);
            ans=min(ans,min(t[t[now].rc].minn,t[now].rc==0?inf:t[t[now].rc].mi-t[now].val));
            //这里还是要注意t[now].val-max(t[t[now].lc].ma,maxx) 可能为0而导致结果错误。
            ans=min(ans,(t[now].lc==0&&maxx==-inf)?inf:t[now].val-max(t[t[now].lc].ma,maxx));
            now=t[now].lc;
        }
        //这里维护一个最大值,是因为找到某些符合要求的b的时候,需要找一个最大的a
        else maxx=max(maxx,t[now].val),now=t[now].rc;
    }
    return ans;
}

void init()
{
     t[0].minn=inf;
     t[0].ma=-inf;
     t[0].mi=inf;
    _insert(root,-inf);
    _insert(root,inf);
}

signed main(void)
{
    int q,op,x;
    init();
    scanf("%lld",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%lld%lld",&op,&x);
        if(op==1)
        {
            _insert(root,x);
        }
        else if(op==2)
        {
            del(root,x);
        }
        else
        {
            int pre=get_front(x);
            if(pre!=-inf)
            {
                int prepre=get_front(pre);
                {
                    if(pre+prepre>x)
                    {
                        printf("Yes\n");
                        continue;
                    }
                }
            }
            if(find_val(x)<x)
            {
                printf("Yes\n");
                continue;
            }
            printf("No\n");
        }
    }
    return 0;
}


动态开点线段树:
离线加离散化也可。
线段树上,叶子节点维护值为 pos 的数与其前面的最近的数的差。
pushup的时候维护最小值。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
//#define lc (cnt<<1)
//#define rc (cnt<<1|1)
#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxm=100100;
const int up=1e9;

struct node
{
    int lc,rc;
    int val;
}t[maxn*35];
int root,cnt;
map<int,int>mp;

int newnode(void)
{
    cnt++;
    t[cnt].lc=t[cnt].rc=0;
    return cnt;
}

void pushup(int p)
{
    t[p].val=min(t[t[p].lc].val,t[t[p].rc].val);
}

int change(int p,int l,int r,int pos,int val)
{
    if(!p) p=newnode();
    if(l==r)
    {
        t[p].val=val;
        return p;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) t[p].lc=change(t[p].lc,l,mid,pos,val);
    else t[p].rc=change(t[p].rc,mid+1,r,pos,val);
    pushup(p);
    return p;
}

int ask(int p,int l,int r,int pos)
{
    if(!p) return inf;
    if(l==r) return t[p].val;
    int mid=(l+r)>>1;
    if(pos<=mid) return min(ask(t[p].lc,l,mid,pos),t[t[p].rc].val);
    else return ask(t[p].rc,mid+1,r,pos);
}

void add(int x)
{
    mp[x]++;
    if(mp[x]==1)
    {
        auto it=mp.lower_bound(x);
        it++;
        if(it!=mp.end()&&it->second==1)
            root=change(root,1,up,it->first,it->first-x);
        it--;
        int pre=-inf;
        if(it!=mp.begin()) pre=(--it)->first;
            root=change(root,1,up,x,x-pre);
    }
    else if(mp[x]==2) root=change(root,1,up,x,0);
}

void del(int x)
{
    auto it=mp.lower_bound(x);
    mp[x]--;
    int pre=-inf;
    if(it!=mp.begin()) pre=prev(it)->first;
    if(mp[x]==0)
    {
        if(++it!=mp.end()&&it->second==1)
            root=change(root,1,up,it->first,it->first-pre);
        root=change(root,1,up,x,inf);
        mp.erase(x);
    }
    else if(mp[x]==1) root=change(root,1,up,x,x-pre);

}

void ans(int x)
{
    auto it=mp.lower_bound(x);
    if(it!=mp.begin())
    {
        int a=(--it)->first;
        if(it!=mp.begin())
        {
            int b=(--it)->first;
            if(a+b>x)
            {
                printf("Yes\n");
                return ;
            }
        }
    }
    if(ask(root,1,up,x)<x) printf("Yes\n");
    else printf("No\n");
    return ;
}

int main(void)
{
    t[0].val=inf;
    int q,op,x;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&op,&x);
        if(op==1) add(x);
        else if(op==2) del(x);
        else ans(x);
    }
    return 0;
}