题目链接:
题目大意:
给出一棵完全二叉树,告诉你出口在某一层的哪些点的子树中(出口一定在树的最后一层),问它给出的提示是否矛盾,如果不矛盾,能否判断出哪个点是出口
题目分析:
首先它给出不管是哪一层的点,都可以换算到最后一层,利用二叉树的性质当前层的l-r的区间,对应到下一层就是2*l-1到2*r,那么就可以一直推到最底层,然后把真的条件,可以换成两个假的条件,然后最后就可以得到所有假的区间,然后对这些区间按照区间左边界排序,然后遍历所有区间,记录当前最左的目前认为是出口的值,然后统计所有可能是出口的值的总数,每次扫描到一个区间,因为它的左边界最小,所以如果当前的最左可能是出口到左边界的间隔不为0,那么这段就判断是出口,t统计,然后更新最左可能是入口的值到达当前区间的右边界加1,注意是可能,之前的点都已经判断为一定是出口,或一定不是,那么扫描到最后,如果t==0,那么说明没有出口了,所以矛盾了,如果t>1.那么说明没有唯一解,其余情况输出维护的那个答案即可
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAX 57
#define M 400007
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;
int h,q;
LL sum[MAX];
LL two[MAX];
void init ( )
{
two[1] = 1;
for ( int i = 2 ; i < MAX ; i++ )
two[i] = two[i-1]<<1;
sum[0] = 0;
for ( int i = 1 ; i < MAX ; i++ )
sum[i] = sum[i-1] + two[i];
}
PII p[M];
int cnt;
int main ( )
{
int a,f;
LL l,r;
init();
while ( ~scanf ( "%d%d" , &h , &q ) )
{
cnt = 0;
while ( q-- )
{
scanf ( "%d%I64d%I64d%d" , &a , &l , &r , &f );
l -= sum[a-1];
r -= sum[a-1];
int temp = a;
while ( temp < h )
{
l = l*2-1;
r = r*2;
temp++;
}
if ( f == 0 ) p[cnt++] = make_pair ( l , r );
else
{
if ( l > 1 )
p[cnt++] = make_pair ( 1 , l-1 );
if ( r < two[h] )
p[cnt++] = make_pair ( r+1 , two[h] );
}
}
sort ( p , p+cnt );
//cout << p[0].first << " " << p[0].second << endl;
LL ans = -1 , loc = 1 , t = 0;
for ( int i = 0 ; i < cnt ; i++ )
{
if ( p[i].first > loc )
{
ans = loc;
t += p[i].first - loc;
}
loc = max ( loc , p[i].second+1 );
}
if ( two[h] >= loc )
{
ans = loc;
t += two[h] - loc + 1;
}
//cout << "YES : " << t << endl;
if ( t == 0 )
puts ("Game cheated!");
else if ( t > 1 )
puts ("Data not sufficient!");
else printf ( "%I64d\n" , ans + sum[h-1]);
}
}