2020牛客多校第二场 H题Happy Triangle(动态开点线段树)
2020牛客多校第二场 H题Happy Triangle(动态开点线段树)
题意:q次询问,3种操作,1:加入一个x,2:删除一个x,3,问能否在加入的边中找两条与x组成3角形。
题解:设三角形3边a,b,x;
其1:a+b>x;
其2:abs(a-b)<x;
由于其二的特性,我们要尽量找靠的近的a与b既可,例如,a与b间有一个数c,如果a,b满足公式,那c+b>x,
b-c<x,所以选c不会影响答案,即可以贪心的找靠在一起的边,那么要满足一条我们至少要使b>=x/2+1,这样a+b才能满足第一个条件(第一的b要特判),然后我们就找大于b的最小的间隔距离(用动态开点线段树既可)。
#include<iostream>
#include<cstring>
#include<map>
using namespace std;
#define ll long long
const ll maxn=1e9+7,inv=2e9+7;
ll q,op,x,k,cnt;
ll ans[3000007],lc[3000007],rc[3000007];
map<ll,ll>ma;
void up(ll &k,ll l,ll r,ll pos,ll z){
if(!k){
k=++cnt;
}
if(l==r){
ans[k]=z;
return;
}
ll mid=(l+r)/2;
if(pos<=mid)up(lc[k],l,mid,pos,z);
else{
up(rc[k],mid+1,r,pos,z);
}
ans[k]=min(ans[lc[k]],ans[rc[k]]);
}
void add(ll x){
ma[x]++;
auto it=ma.lower_bound(x),it2=it;
it++;
it2--;
if(it->first!=inv){
if(it->second<=1){
up(k,1,maxn,it->first,it->first-x);
}
else{
up(k,1,maxn,it->first,0);
}
}
if(ma[x]<=1){
up(k,1,maxn,x,x-it2->first);
}
else{
up(k,1,maxn,x,0);
}
}
void del(ll x){
auto it=ma.lower_bound(x),it2=it;
it++;
it2--;
ma[x]--;
if(ma[x]==0){
ma.erase(x);
up(k,1,maxn,x,inv);
if(it->first!=inv&&it->second==1){
up(k,1,maxn,it->first,it->first-it2->first);
}
}
else if(ma[x]==1){
up(k,1,maxn,x,x-it2->first);
}
}
ll findz(ll k,ll l,ll r,ll ql,ll qr){
if(ql<=l&&r<=qr){
return ans[k];
}
ll mid=(l+r)/2,ans1=inv,ans2=inv;
if(ql<=mid){
ans1=findz(lc[k],l,mid,ql,qr);
}
if(qr>mid){
ans2=findz(rc[k],mid+1,r,ql,qr);
}
return min(ans1,ans2);
}
ll query(ll x){
ll qans=inv;
auto it=ma.lower_bound(x/2+1),it2=it;
it2--;
if(it->first==inv){
return inv;
}
if(it->second>=2){
qans=0;
}
if(it->first+it2->first>x){
qans=min(it->first-it2->first,qans);
}
it++;
if(it->first==inv){
return qans;
}
else{
qans=min(qans,findz(k,1,maxn,it->first,maxn));
}
return qans;
}
int main(){
memset(ans,inv,sizeof(ans));
scanf("%lld",&q);
ma[-inv]++;
ma[inv]++;
while(q--){
scanf("%lld%lld",&op,&x);
if(op==1){
add(x);
}
if(op==2){
del(x);
}
if(op==3){
if(query(x)<x){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}
}