题意:
有n个砝码,每个砝码有一个权值k(i),表示重量为1/2k(i),
判断是否有可能将砝码分为两组,满足每组的重量都>=1/2
如果有解则输出方案
数据范围:n<=1e5,1<=k(i)<=1e9
解法:
有点类似二进制位,两个底位凑成一个高位。
这题只是多了一个倒数,两个1/2p可以合并为一个1/2p-1,问最后是否至少两个1/2,
开一个小根堆,将所有数丢入堆中,每次贪心地取出最小的尝试两个合并,看看最后是否至少两个1/2即可。
虽然k(i)的大小为1e9,但是n只有1e5,每次合并都会减少一个砝码,理论上最多合并1e5次,
总复杂度O(nlogn)
这题还需要输出方案,考虑并查集,直接将合并的砝码合并为一个集合就行了,非常方便。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
struct Node{
int x,id;
bool operator<(const Node& a)const{
return x<a.x;
}
};
int pre[maxm];
int n;
int ffind(int x){
return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
int T;scanf("%d",&T);
int cas=1;
while(T--){
scanf("%d",&n);
priority_queue<Node>q;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
q.push({x,i});
}
while(q.size()>=2&&q.top().x>=2){
Node a=q.top();q.pop();
Node b=q.top();
if(a.x==b.x){
q.pop();
int rt=pre[ffind(a.id)]=ffind(b.id);
q.push({a.x-1,rt});
}
}
printf("Case %d: ",cas++);
if(q.size()>=2){
puts("YES");
}else{
puts("NO");
continue;
}
int rt=q.top().id;
for(int i=1;i<=n;i++){
if(ffind(i)==rt){
printf("0");
}else{
printf("1");
}
}
puts("");
}
return 0;
}