题意:有一颗无穷大的满二叉树,每一次我们向树上的某一层的某个节点放置奖牌,奖牌排斥的条件为:从该节点到根的路径上无其他奖牌,如果能放置则放置,否则不放跳过,并且要按照输入的顺序放置,每次输出Yes或者No
题解:先找到最大的深度K,那么说明如果全部放置在第K层,那么会占据一个结点,如果是K-1层则会占据2个结点,所以把问题转化为了一共有2的K次方的点可以放,把放在X层的点转化到K层,也就是占据2的K-X次方的点,所以每次只会加上一个2的K-X次方,如果加上之后大于了2的K次,则说明不能放,则减去,否则可以放。
相当于是一个简单的2进制的加法和减法,每次在一位上加1,如果不能放则减去1抵消加上的1。因为最大的层数是10的9次,所以用map来实现这个模拟;但是还需要一些优化,如果现在最多放2的K次,当前放了2的K-1次,那么就可以把K变成K-1,删除那个K-1次位上的1,意思就是如果现在最高位已经是1了,那么就可以不用考虑这一位,超过一半被使用,那么就可以只考虑剩下的一半,把容量减半
代码:
#include<bits/stdc++.h>
#define N 500005
#define P pair<int,int>
using namespace std;
typedef long long ll;
const int M=1e9+7;
const int inf=1e9+7;
int a[N];
map<int,int>mp;
void add(int x)
{
mp[x]++;
while(mp[x]==2){
mp[x+1]++;
mp[x++]=0;
mp.erase(x-1);
}
}
void sub(int x)
{
int y=x;
while(!mp[y]){
mp[y++]=1;
}
mp[y]=0;
mp.erase(y);
}
int main()
{
int n,tot=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
tot=max(tot,a[i]);
}
map<int,int>::iterator it;
int num=tot;
for(int i=0;i<n;i++){
int x=tot-a[i];
if(x>num||x==num&&mp.size()){
printf("No\n");
continue;
}
add(x);
it=mp.end();
it--;
if(it->first>num||(it->first==num&&mp.size()>1)){
printf("No\n");
sub(x);
}
printf("Yes\n");
if(x==num){
num=-1;
mp.clear();
}
while(!mp.empty()){
it=mp.end();
it--;
if(it->first>num)mp.erase(it);
else if(it->first==num-1){
num--;
mp.erase(it);
}
else break;
}
}
return 0;
}