【思路】
把输入的每一个区间都下放到树的最后一层上,把树的最后一层看成一个序列,问题就转换成了求若干区间的交集和并集,交集比较好求,维护左右断点值即可。求并集需要将区间端点离散化后再处理,参考了大佬的代码,比较诡异,不是很懂正确性。还要注意的是,离散化完以后是一个点,对应原来的序列可能是一个区间,也可能是一个点,需要特殊判断,所以在离散化的时候不仅需要把区间的左右端点加入,还要把它们的前一个点和后一个点也加进来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int maxn=1e5+5;
int h,q,d,ok;
ll pw[55],le,ri,maxl,minr;
ll v[maxn*6];int tot;
pll a[maxn];int cnt;
int sum[maxn*6];
int main(){
pw[0]=1;
for(int i=1;i<55;++i) pw[i]=pw[i-1]*2;
scanf("%d%d",&h,&q);
maxl=pw[h-1],minr=pw[h]-1;
while(q--){
scanf("%d%lld%lld%d",&d,&le,&ri,&ok);
while(d<h){
le=le*2;
ri=ri*2+1;
++d;
}
v[++tot]=le-1;v[++tot]=le;v[++tot]=le+1;
v[++tot]=ri-1;v[++tot]=ri;v[++tot]=ri+1;
if(ok){
maxl=max(maxl,le);
minr=min(minr,ri);
}
else{
a[++cnt]=pll(le,ri);
}
}
if(maxl>minr){
puts("Game cheated!");
return 0;
}
sort(v+1,v+1+tot);
tot=unique(v+1,v+1+tot)-(v+1);
for(int i=1;i<=cnt;++i){
int pl=lower_bound(v+1,v+1+tot,a[i].first)-v;
int pr=lower_bound(v+1,v+1+tot,a[i].second)-v;
++sum[pl];
--sum[pr+1];
}
for(int i=1;i<=tot;++i) sum[i]+=sum[i-1];
for(int i=1;i<=tot;++i){
sum[i]=min(sum[i],1);
sum[i]+=sum[i-1];
}
int pl=lower_bound(v+1,v+1+tot,maxl)-v;
int pr=lower_bound(v+1,v+1+tot,minr)-v;
if(pr-pl+1==sum[pr]-sum[pl-1]){
puts("Game cheated!");
}
else if(pr-pl+1>sum[pr]-sum[pl-1]+1){
puts("Data not sufficient!");
}
else{
for(int i=pl;i<=pr;++i){
if(sum[i]==sum[i-1]){
if(i==1){
if(v[i]+1==v[i+1]) printf("%lld\n",v[i]);
else puts("Data not sufficient!");
}
else if(i==tot){
if(v[i-1]+1==v[i]) printf("%lld\n",v[i]);
else puts("Data not sufficient!");
}
else{
if(v[i-1]+1==v[i] && v[i]+1==v[i+1]) printf("%lld\n",v[i]);
else puts("Data not sufficient!");
}
break;
}
}
}
return 0;
}