2020牛客暑期多校训练营(第二场) H-Happy Triangle(动态开点线段树)

H-Happy Triangle

2020牛客暑期多校训练营(第二场) H-Happy Triangle(动态开点线段树)_第1张图片

题意:3种操作

1、往多重集 插入一个x

2、从多重集合里 删除一个x

3、询问多重集和是否有两个值 a、b  使得 a、b、x构成一个合法的三角形。

官方题解:

2020牛客暑期多校训练营(第二场) H-Happy Triangle(动态开点线段树)_第2张图片

做法:对所有的数按序排成一列,当查询一个x时  ,对于合法的a、b (ax(两边之和大于第三条边),且取a、b是相邻的时候是最 容易得到解,于是 对 x/2+1 二分找到b的位置,那么大于b的位置 后面的值均满足a+b>x的条件。

构成三角形的条件还有一条:任意两边之差小于第三边。b-a

这里线段树我用动态开点的做法维护

对于1、2 的操作 用map维护,至于维护3:插入一个新的x时,就得维护x在线段树有序  序列中  减去前一个位置的差,后一个位置 减去x的值。

说起来我都有点绕不开了。

总之两句句话,

1、线段树维护相邻数之间的差

2、新插入一个数 会影响两个位置的值,删除一个数也会影响两个位置的值1

#include
using namespace std;
const int N=1e6+10,MAX=1e9;
int mi[40*N], ls[40*N], rs[40*N], cnt, rt;
mapmp;
void update(int &id, int l, int r, int pos, int val)
{
    if(id==0) id = ++cnt, mi[id] = val;
    if(l == r){mi[id] = val;return ;}
    int mid = l + r >> 1;
    if(pos <= mid) update(ls[id], l, mid , pos, val);
    else update(rs[id], mid + 1, r, pos, val);
    int ans = 2e9;
    if(ls[id]) ans = min(ans, mi[ls[id]]);
    if(rs[id]) ans = min(ans, mi[rs[id]]);
    mi[id] = ans;
}

int ask_min(int id,int l,int r,int ql,int qr)
{
    if(r < l || !id) return 2e9;
    if(ql <= l && r <= qr) return mi[id];
    int ans = 2e9;
    int mid = l + r >> 1;
    if(ql <= mid) ans = min(ans, ask_min(ls[id], l, mid, ql, qr));
    if(qr > mid) ans = min(ans, ask_min(rs[id], mid+1, r, ql, qr));
    return ans;
}

void add(int x)
{
    mp[x]++;
    if(mp[x]==1){
        auto it = mp.lower_bound(x);
        ++it;
        if(it != mp.end() && it->second == 1){//后面一个数减去现在这个数
            update(rt, 0, MAX, it->first, it->first - x);
        }
        --it;
        int l = -2e9;
        if(it != mp.begin()) l = (--it)->first;//现在这个数减去前面那个数
        update(rt, 0, MAX, x, x - l);
    }
    else if(mp[x] == 2) update(rt, 0, MAX, x, 0);//有两个相同的了
}

void del(int x){

	auto it=mp.lower_bound(x);
	mp[x]--;
	int l=-1e9;
	if(it!=mp.begin()){
		l=(--it)->first;
		++it;
	}
	if(mp[x]==0){
		if((++it)!=mp.end() && it->second==1)
			update(rt,0,MAX,it->first,it->first-l);
		update(rt,0,MAX,x,2e9);
		mp.erase(x);
	}
	else if(mp[x]==1)update(rt,0,MAX,x,x-l);
}

int ask(int x)
{
    auto it = mp.lower_bound(x/2+1);
    if(it == mp.end()) return 2e9;
    if(it->second > 1) return it->first;

    if(it != mp.begin()){
        auto l = it; --l;
        if(l -> first + it -> first > x) return it->first;
    }

    if((++it) != mp.end()) return it->first;

    return 2e9;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int q;scanf("%d", &q);
    while(q--){
        int op, x;
        scanf("%d%d", &op, &x);
        if(op == 1) add(x);
        if(op == 2) del(x);
        if(op == 3) {
            if(ask_min(1, 0, MAX, ask(x), 1e9) < x) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

 

你可能感兴趣的:(牛客题解,数据结构---线段树)