Tree DP 。很有意思的一个题目。给定了2^n个人每轮比赛的结果,然后推测每个人可能的最高排名与最低排名。由于给的是每轮比赛的结果,所以题目在数据的读入与处理方面比较麻烦。如果编号为i的人赢了编号为j的人,那么我们连有向边(i,j),即j为i的孩子。这样我们只需要处理出每轮比赛的两个人之间的关系即可。由于数据的最后一个肯定是树的根节点,然后我们从后往前扫,我们就知道每一对数之间的关系了。此外我们需要将那些不在给的数据中的节点也给添加边关系。我们先计算出每个人祖先节点的个数,以及子孙节点的个数(也就是最高排名与最低排名)。对于每个查询,我们直接输出结果。
/*
*author : csuchenan
*prog : 1241
*Algorithm : Tree DP DFS
*notice : build the tree
*csuchenan 1241 Accepted 188K 16MS C++ 2033B
*/
#include <cstdio>
#include <cstring>
#include <vector>
using std::vector ;
#define maxn 270
vector<int> G[maxn] ;
int dp[maxn] ;
int son[maxn] ;
int n ;
int total[maxn] ;
int root ;
void init(){
int num = 1<<n ;
for(int i = 1 ; i <= num ; i ++){
G[i].clear() ;
}
memset(dp , 0 , sizeof(dp)) ;
memset(son , 0 , sizeof(son)) ;
}
bool read(){
scanf("%d" , &n) ;
if(n == 0)
return 0 ;
int num = 1 ;
num = 1<< n ;
num = num - 1 ;
init() ;
for(int i = 1 ; i <= num ; i ++){
scanf("%d" , &total[i]) ;
}
root = total[num] ;
for(int i = num - 1 ; i >= 1 ; i = i - 2){
G[ total[i] ].push_back( total[i - 1] ) ;
G[ total[i - 1] ].push_back( total[i] ) ;
}
for(int i = 1 ; i <= (num + 1)>>1 ; i ++){
if(total[i]%2){
G[total[i]].push_back(total[i] + 1) ;
G[total[i] + 1].push_back(total[i]) ;
}
else{
G[total[i]].push_back(total[i] - 1) ;
G[total[i] - 1].push_back(total[i]) ;
}
}
return n ;
}
void debug(){
int num = 1 ;
int i = n ;
num = 1<<n ;
for(int i = 1 ; i <= num ; i ++){
printf("%d : " , i) ;
for(int j = 0 ; j != G[i].size() ; j ++){
printf(" %d " , G[i][j]) ;
}
printf("\n") ;
}
}
void dfs(int v , int f){
dp[v] = dp[f] + 1 ;
for(int i = 0 ; i != G[v].size() ; i ++){
int u = G[v][i] ;
if(u == f)
continue ;
dfs(u , v) ;
son[v] += son[u] + 1 ;
}
}
void solve(){
dp[0] = 0 ;
dfs(root , 0) ;
int Q ;
int x ;
scanf("%d" , &Q) ;
for(; Q-- ;){
scanf("%d" , &x) ;
printf("Player %d can be ranked as high as %d or as low as %d.\n" , x , dp[x] , (1<<n) - son[x] ) ;
}
printf("\n") ;
}
int main(){
while(read()){
//debug() ;
solve() ;
}
return 0 ;
}