题解:P9293 [ROI 2018] Addition without carry
[ROI 2018] Addition without carry题解
毛子的题目就是牛鼻。
首先容易知道「美丽的集合」一定是对于每两个数,在二进制下无交。
也就是说答案
我们先确定答案的最高位
设
一直检验直到序列为空。
这样就可以判断一个
首先很容易发现,对于一个数都需要消耗一个
(如果当
好,现在我们明白
于是现在就有个方法了,我们令
对于
然后我们判断答案最高是否可以为
若返回
若假设算
对于返回为
对于返回为
若有一个返回
所以目前只需要维护一下算
对于删除最高位的操作可以这样,我们把
代码:
struct _{
std::pair<int,int>mx;/*最大值和所对应位置*/int cnt/*存在个数*/,lazy;
}tree[maxn<<2];
#define mid ((l+r)>>1)
void put_tag(int k,int val){tree[k].mx.first+=val;tree[k].lazy+=val;}
void push_up(int k){tree[k].mx=std::max(tree[k<<1].mx,tree[k<<1|1].mx),tree[k].cnt=tree[k<<1].cnt+tree[k<<1|1].cnt;}
void push_down(int k){if(tree[k].lazy)put_tag(k<<1,tree[k].lazy),put_tag(k<<1|1,tree[k].lazy),tree[k].lazy=0;}
int query(int k,int l,int r,int x,int y){//查询有多少个存在
if(x>y)return 0;
if(x<=l&&r<=y)return tree[k].cnt;
push_down(k);int res=0;
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
return res;
}
void change(int k,int l,int r,int x,int y,int z){//修改一个点,也就是将存在性修改,同时改一下对于h的贡献
if(l==r)return tree[k].mx.first=y,tree[k].cnt=z,void();
push_down(k);
if(x<=mid)change(k<<1,l,mid,x,y,z);
else change(k<<1|1,mid+1,r,x,y,z);
push_up(k);
}
void add(int k,int l,int r,int x,int y,int val){//加入一个数肯定会将一段的排名+1
if(x>y)return;
if(x<=l&&r<=y)return put_tag(k,val),void();
push_down(k);
if(x<=mid)add(k<<1,l,mid,x,y,val);
if(y>mid)add(k<<1|1,mid+1,r,x,y,val);
push_up(k);
}
void build(int k,int l,int r){
if(l==r)return tree[k]={{-1e9,l},0,0},void();
build(k<<1,l,mid);build(k<<1|1,mid+1,r);push_up(k);
}
std::set<int,std::greater<int> >st;//greater可能是取反的意思,反正这样写才是优先取大的数
int n,tmp[maxn],maxx,a[maxn],tot,net[maxn];char s[maxn];std::basic_string<int>E[maxn];bool ans[maxn<<1];
void insert(int k){add(1,1,tot,1,k-1,1);change(1,1,tot,k,a[k]+query(1,1,tot,k+1,tot),1);st.insert(k);}
void erase(int k){add(1,1,tot,1,k-1,-1);change(1,1,tot,k,-1e9,0);st.erase(k);}
void print(){
int sb=n+maxx;
for(;!ans[sb];)sb--;
for(int i=sb;i>=0;--i)putchar(ans[i]+'0');
}
bool solve(int p){
if(st.size()==0)return print(),1;
auto[h,id]=tree[1].mx;
if(p<h)return 0;
int now=h;std::basic_string<int>sss;
for(;;){
int x=*st.begin();
erase(x);sss+=x;ans[now--]=1;
if(x==id)break;
}
// fprintf(stderr,"%d",net[id]);
if(net[id]&&net[id]!=1e9)insert(net[id]);
if(solve(now))return 1;
if(net[id]&&net[id]!=1e9)erase(net[id]);
if(p==h){
for(int i=h;i>now;--i)ans[i]=0;
for(int x:sss)insert(x);
return 0;
}
ans[now+1]=0;ans[h+1]=1;//这里由于[h,h-i+1]和[h+1,h-i+2]又两个位置不一样
return solve(now+1);
}
signed main(){
read(n);
for(int i=1;i<=n;++i){
scanf("%s",s+1);int len=strlen(s+1);std::reverse(s+1,s+len+1);maxx=std::max(maxx,len);
for(int j=1;j<=len;++j)if(s[j]=='1')E[j]+=i,tot++;
}
int lp=0;
for(int i=1;i<=maxx;++i){
std::sort(E[i].begin(),E[i].end(),[](int x,int y){return tmp[x]<tmp[y];});//若当前这位一样,还有让后面的小,这样才能让a是单增的
for(int x:E[i])net[++lp]=tmp[x],tmp[x]=lp,a[lp]=i-1;
}
build(1,1,tot);
for(int i=1;i<=n;++i)insert(tmp[i]);
solve(n+maxx);
return 0;
}