题意:
有一颗高为h的完整二叉树,出口在某一个叶子节点上。
有n次问答,每次你问一个区间,系统都会告诉你出口是否在这个区间内。
而你的任务是根据n次问答,判定出:系统作弊、出口位置、条件不足以判断出口。
思路:
看了很多其它的题解,最后还是感觉杰哥的思路来的简单。
首先把所给的每个区间都映射到叶子节点的区间。
1.求所有为真的区间,并求出它们的交集,记为a。(a为一定存在的区间)如果交集为空,说明系统作弊。
2.把所有为假的区间映射为两个真的区间,记为b。
3.对b求并集,并求出b对于叶子节点的补集,记为c集合。(c为可能存在的区间)
4.最后求a和c的交集。若交集有多个,则条件不足。若没有交集,则系统作弊。只有一个交集,则输出该交集。
code:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1e5+5;
typedef long long LL;
struct PP {
LL l, r;
bool operator < (const PP &cmp) const {
return l < cmp.l;
}
};
int h, q;
LL L, R;
vector <PP> a, b, c;
void solve() {
sort(a.begin(), a.end());
LL maxl = L, minr = R;
for(int i = 0;i < a.size(); i++) {
maxl = max(maxl, a[i].l);
minr = min(minr, a[i].r);
}
//cout<<"maxl = "<<maxl<<" minr = "<<minr<<endl;
if(maxl > minr) {
puts("Game cheated!");
return ;
}
sort(b.begin(), b.end());
LL tl = 0, tr = 0;
c.push_back((PP){L-1, L-1});
for(int i = 0;i < b.size(); i++) {
if(i == 0)
tl = b[i].l, tr = b[i].r;
else {
if(b[i].l > tr) {
c.push_back((PP){tl, tr});
tl = b[i].l, tr = b[i].r;
}
else {
tr = max(tr, b[i].r);
//tl = min(tl, b[i].l);
}
}
if(i == b.size()-1) c.push_back((PP){tl, tr});
}
c.push_back((PP){R+1, R+1});
b.clear();
for(int i = 0;i+1 < c.size(); i++) {
if(c[i+1].l-c[i].r-1 > 0)
b.push_back((PP){c[i].r+1, c[i+1].l-1});
}
//for(auto &it : b) cout<<it.l<<" "<<it.r<<endl;
LL cnt = 0;
LL res;
for(int i = 0;i < b.size(); i++) {
LL t1 = max(maxl, b[i].l), t2 = min(minr, b[i].r);
if(t1 <= t2){
cnt += t2-t1+1;
res = t1;
}
}
if(cnt == 0)
puts("Game cheated!");
else if(cnt > 1)
puts("Data not sufficient!");
else
printf("%I64d\n", res);
}
int main() {
scanf("%d%d", &h, &q);
int ti = 1;
L = R = 1;
while(ti < h) {
L *= 2;
R = R*2+1;
ti++;
}
//cout<<"L = "<<L<<" R = "<<R<<endl;
for(int i = 0;i < q; i++) {
int th, ok;
LL l, r;
scanf("%d%I64d%I64d%d", &th, &l, &r, &ok);
while(th < h) {
l <<= 1;
r = r*2+1;
th++;
}
if(ok) a.push_back((PP){l, r});
else b.push_back((PP){l, r});
}
solve();
return 0;
}