无进位加法

先考虑一个多项式做法:
根据套路从高到低位确定答案。
令当前位\(i\)\(0\),贪心的让后面合法。显然把\(1~i-1\)位都设成\(1\)进行判定。
考虑当前\(a\)的最大值\(v\),扫到第\(j\)位。
如果\(v\)的长度大于\(j\)显然无解
否则如果\(v\)的第\(i\)位为\(0\),则删除最大值继续判定。
如果\(v\)的第\(i\)为为\(1\),则把这一位的\(1\)和前导\(0\)删除后继续判定
取出最大值的正确性可以感性理解,就是如果当前位不覆盖最大值,则后面的位更难覆盖。
考虑加速比较的过程。
我们需要支持一个数据结构,要求快速插入,删除最大值,取出最大值。
使用堆+二分+哈希可以做到\(O(n^2\log_2^2n)\)
但是我们比较只涉及两个开头为\(1\)的某个字符串的后缀(还要把前面补\(0\))比较。
使用基数排序/二分+哈希即可将所有后缀排序。
比较时间复杂度降低至\(O(n^2\log_2n)\)
70分代码如下:

#include<bits/stdc++.h>
using namespace std;
#define N 500010
#define int long long
#define m1 998244353
#define m2 1000000007
int n,l[N],ct,p1[N],p2[N],h1[N],h2[N],cc,ans[N],cv,la[N],rk[N],st[N],lv[N],dl[N],tp[N];
char c[N],ch[N*30];
vector<int>v[N],id[N];
struct no{
	int l,x,le;
};
struct nn{
	int l,r,p;
}p[N];
int operator <(no x,no y){
	return rk[x.l]<rk[y.l];
}
int v1(int l,int r){
	return (h1[r]-h1[l-1]+m1)%m1*p1[ct-r]%m1;
}
int v2(int l,int r){
	return (h2[r]-h2[l-1]+m2)%m2*p2[ct-r]%m2;
}
int operator <(nn x,nn y){
	if(x.r-x.l!=y.r-y.l)
		return x.r-x.l<y.r-y.l;
	int p=x.r-x.l+1;
	int l=0,r=p-1,ans=0;
	while(l<=r){
		int md=(l+r)/2;
		if(v1(x.l,x.l+md)==v1(y.l,y.l+md)&&v2(x.l,x.l+md)==v2(y.l,y.l+md)){
			ans=md;
			l=md+1;
		}
		else
			r=md-1;
	}
	if(ans==p-1)
		return x.r-x.l+1<y.r-y.l+1;
	else
		return ch[x.l+ans+1]<ch[y.l+ans+1];
}
signed main(){
	scanf("%lld",&n);
	p1[0]=p2[0]=1;
	for(int i=1;i<N;i++){
		p1[i]=p1[i-1]*5%m1;
		p2[i]=p2[i-1]*7%m2;
	}
	int ml=0;
	for(int i=1;i<=n;i++){
		scanf("%s",c);
		l[i]=strlen(c);
		reverse(c,c+l[i]);
		int ll=0;
		for(int j=0;j<l[i];j++)
			if(c[j]=='1'){
				cv++;
				lv[cv]=j+1;
				st[i]=max(st[i],cv);
				tp[i]=st[i];
				id[j].push_back(cv);
				p[++cc]=(nn){ct+1,ct+j+1,cv};
				if(ll)
					la[cv]=ll;
				ll=cv;
			}
		for(int j=0;j<l[i];j++){
			v[i].push_back(c[j]-'0');
			ch[++ct]=c[j]-'0';
		}
		ml=max(ml,l[i]);
	}
	reverse(ch+1,ch+ct+1);
	for(int i=1;i<=ct;i++){
		h1[i]=(h1[i-1]+p1[i]*ch[i])%m1;
		h2[i]=(h2[i-1]+p2[i]*ch[i])%m2;
	}
	for(int i=1;i<=cc;i++){
		int a=p[i].l,b=p[i].r;
		p[i].l=ct-b+1;
		p[i].r=ct-a+1;
	}
	sort(p+1,p+cc+1);
	int cv=0;
	for(int i=1;i<=cc;i++){
		int l1=p[i-1].l,r1=p[i-1].r,l2=p[i].l,r2=p[i].r;
		if(i!=1&&v1(l1,r1)==v1(l2,r2)&&v2(l1,r1)==v2(l2,r2)){
			
		}
		else
			cv++;
		rk[p[i].p]=cv;
	}
	if(n<=3000){
		priority_queue<no>q;
		for(int i=ml+n+1;~i;i--){
			int ok=1,ct=0,cv=0;
			while(!q.empty())
				q.pop();
			for(int j=1;j<=n;j++)
				if(!dl[j]){
					q.push((no){tp[j],j,lv[tp[j]]});
					cv++;
				}
			for(int j=i-1;j;j--){
				if(ct==cv)
					break;
				no x=q.top();
				q.pop();
				if(x.le>j){
					ok=0;
					break;
				}
				else if(x.le==j){
					no y;
					if(la[x.l]){
						y.le=lv[la[x.l]];
						y.x=x.l;
						y.l=la[x.l];
						q.push(y);
					}
					else
						ct++;
				}
				else
					ct++;
			}
			if(ct!=cv)
				ok=0;
			if(ok)
				ans[i]=0;
			else{
				while(!q.empty())
					q.pop();
				for(int j=1;j<=n;j++)
					if(!dl[j])
						q.push((no){tp[j],j,lv[tp[j]]});
				no x=q.top();
				q.pop();
				if(x.le==i){
					if(la[x.l])
						tp[x.x]=la[x.l];
					else
						dl[x.x]=1;
				}
				else
					dl[x.x]=1;
				ans[i]=1;
			}
		}
		int ok=0;
		for(int i=ml+n+1;i;i--){
			if(ans[i])
				ok=1;
			if(ok)
				printf("%lld",ans[i]);
		}
	}
}

想要获得正解,需要发现更多性质。
有部分分\(a_i=2^k\)。这个部分分的特点:在上面算法,讨论\(a_i\)是否等于\(0\)是不必要的。因为如果覆盖当前位置,当前位置的元素就会被删除。
还是考虑判定当前位是否能为1。把所有数按照\(k\)从大到小排序。
\(mx_i=L_i+i-1\)的最大值,其中\(L_i\)为第\(i\)位的长度。则只要判定当前位\(mx\)是否小于\(x\),即可判定
这是因为如果我们用一个全是\(1\)的前缀贪心覆盖当前点,则到当前点\(j\)的串长度为\(i-j\)
推一下就知道这样子是正确的。
维护一个指针\(pt\)表示判定到哪里。如果当前位置放置\(1\),则\(pt--\)否则不变。
使用\(mx_pt\)即可判定当前位置是否一定要放\(1\)
一般情况也是把所有有用的后缀(开头为\(1\))从大到小排。
在一般情况,令\(mx=L_i+i-1\)的最大值,则答案长度的上界是\(2^{mx+1}\),下界是\(2^{mx}\),把所有数变为\(2^{mx+1}\)\(2^{mx}\)就可以证明。
考虑我们的贪心过程。如果我们当前的\(mx<\)的话,则前面的位都会被删除。
当前位由于我们放置的\(1\)等于当前位的最大值的最高位,所以把当前位删除首位的\(1\),前面位全部删除即可继续判定。
可以递归。
\(pd(B)\)表示答案长度最大为\(B\)是否合法
我们判定答案长度是否能是\(2^{mx}\),把当前位\(k\)删除首位的\(1\),前面位全部删除即可递归\(pd(B-k)\)
可以把当前位删除,当前位的后面一个后缀插入。
判定答案长度是否能是\(2^{mx+1}\),把当前位\(k\)和前面位全部删除即可递归\(pd(B-k+1)\)
使用一个下标为所有后缀排名的线段树维护\(L_i+i-1\),如果删除就是把当前位置置为\(-\inf\),当前位置后面的数\(-1\)
插入就是如果删除就是把当前位置加上\(-\inf\),当前位置后面的数\(+1\)
用线段树二分找到第一个\(=mx\)的位置。
如果当前位置可以更新答案,则前面没卡到上界的位都是取到\(10000...0000\)
时间复杂度不会证明,但是好像是\(O(|S|\log_2|S|)\)
代码非常长,但是是因为内嵌暴力导致的。

#include<bits/stdc++.h>
using namespace std;
#define N 300010
int n,l[N],ct,ans[N],cv,la[N],rk[N],st[N],lv[N],dl[N],tp[N],h1[N],rr[N],cs,ss[N],tt,nx[N];
char c[N],ch[N];
vector<int>id[N],lg[N],vi[N];
struct nn{
	int x,i;
}a[N];
struct nt{
	int x,l;
};
int operator <(nn x,nn y){
	return x.x<y.x;
}
int operator <(nt x,nt y){
	return x.l<y.l;
}
struct no{
	int l,x,le;
};
int operator <(no x,no y){
	return rk[x.l]<rk[y.l];
}
multiset<nt>s;
multiset<nt>::iterator it;
struct sgt{
	int mx[N*4],tg[N*4];
	void pd(int o){
		if(tg[o]){
			tg[o*2]+=tg[o];
			tg[o*2+1]+=tg[o];
			mx[o*2]+=tg[o];
			mx[o*2+1]+=tg[o];
			tg[o]=0;
		}
	}
	void ad(int o,int l,int r,int x,int y,int z){
		if(r<x||y<l)
			return;
		if(x<=l&&r<=y){
			tg[o]+=z;
			mx[o]+=z;
			return;
		}
		pd(o);
		int md=(l+r)/2;
		ad(o*2,l,md,x,y,z);
		ad(o*2+1,md+1,r,x,y,z);
		mx[o]=max(mx[o*2],mx[o*2+1]);
	}
	void qp(int o,int l,int r,vector<int>&vc){
		if(mx[o]<0)
			return;
		if(l==r){
			vc.push_back(l);
			return;
		}
		pd(o);
		int md=(l+r)/2;
		qp(o*2,l,md,vc);
		if(mx[o*2]<mx[1])
			qp(o*2+1,md+1,r,vc);
	}
}t;
void dd(int x){
	t.ad(1,1,cv,x,x,-1e9);
	if(x<cv)
		t.ad(1,1,cv,x+1,cv,-1);
}
void ad(int x){
	t.ad(1,1,cv,x,x,1e9);
	if(x<cv)
		t.ad(1,1,cv,x+1,cv,1);
}
int dfs(int ml){
	if(t.mx[1]<0)
		return 1;
	int B=t.mx[1];
	if(B>ml)
		return 0;
	vector<int>va;
	t.qp(1,1,cv,va);
	int po=va.back();
	va.pop_back();
	for(int i=0;i<va.size();i++){
		int x=va[i];
		dd(x);
	}
	dd(po);
	if(la[ss[po]])
		ad(rk[la[ss[po]]]);
	if(dfs(B-(int)va.size()-1)){
		int sz=va.size(),ct=0;
		for(int i=B;i>=B-sz;i--)
			ans[i]=1;
		return 1;
	}
	if(la[ss[po]])
		dd(rk[la[ss[po]]]);
	if(B+1<=ml&&dfs(B-(int)va.size())){
		int sz=va.size(),ct=0;
		for(int i=B;i>=B-sz;i--)
			ans[i+1]=1;
		return 1;
	}
	for(int i=0;i<va.size();i++){
		int x=va[i];
		ad(x);
	}
	ad(po);
	return 0;
}
signed main(){
	scanf("%lld",&n);
	int ml=0,oo=1,sl=0;
	for(int i=1;i<=n;i++){
		scanf("%s",c);
		int ok=1;
		l[i]=strlen(c);
		reverse(c,c+l[i]);
		lg[i].push_back(0);
		vi[i].push_back(0);
		id[i].push_back(0);
		for(int j=0;j<l[i]-1;j++)
			if(c[j]!='0')
				ok=0;
		if(!ok)
			oo=0;
		rk[i]=l[i];
		sl+=l[i];
		int ll=0,lt=0;
		for(int j=0;j<l[i];j++){
			int va=0;
			cv++;
			lv[cv]=j+1;
			lg[i].push_back(lt);
			lt=cv;
			id[i].push_back(cv);
			if(c[j]=='1'){
				va=1;
				st[i]=max(st[i],cv);
				tp[i]=st[i];
				if(ll){
					la[cv]=ll;
					nx[ll]=cv;
				}
				ll=cv;
			}
			vi[i].push_back(va);
		}
		s.insert((nt){i,l[i]});
		reverse(c,c+l[i]);
		for(int j=0;j<l[i];j++)
			ch[++ct]=c[j]-'0';
		ml=max(ml,l[i]);
	}
	if(oo){
		int mx=0,c=0;
		sort(rk+1,rk+n+1);
		reverse(rk+1,rk+n+1);
		for(int i=n;i;i--){
			h1[i]=max(h1[i+1]+1,rk[i]);
			mx=max(mx,h1[i]);
		}
		int pt=1;
		for(int i=mx;i;i--){
			if(pt<=n&&i<=h1[pt]){
				printf("1");
				pt++;
			}
			else{
				printf("0");
			}
		}
		return 0;
	}
	memset(rk,0,sizeof(rk));
	int cg=0;
	for(int i=1;i<=ml;i++){
		while(!s.empty()){
			nt x=*s.begin();
			if(x.l>=i)
				break;
			s.erase(s.begin());
		}
		int ct=0;
		for(it=s.begin();it!=s.end();it++){
			nt x=*it;
			int p=rk[lg[x.x][i]];
			if(vi[x.x][i])
				p+=1000010;
			a[++ct]=(nn){p,id[x.x][i]};
		}
		sort(a+1,a+ct+1);
		for(int j=1;j<=ct;j++)
			rk[a[j].i]=++cg;
	}
	if(sl<=1000){
		priority_queue<no>q;
		for(int i=ml+n+1;~i;i--){
			int ok=1,ct=0,cv=0;
			while(!q.empty())
				q.pop();
			for(int j=1;j<=n;j++)
				if(!dl[j]){
					q.push((no){tp[j],j,lv[tp[j]]});
					cv++;
				}
			for(int j=i-1;j;j--){
				if(ct==cv)
					break;
				no x=q.top();
				q.pop();
				if(x.le>j){
					ok=0;
					break;
				}
				else if(x.le==j){
					no y;
					if(la[x.l]){
						y.le=lv[la[x.l]];
						y.x=x.l;
						y.l=la[x.l];
						q.push(y);
					}
					else
						ct++;
				}
				else
					ct++;
			}
			if(ct!=cv)
				ok=0;
			if(ok)
				ans[i]=0;
			else{
				while(!q.empty())
					q.pop();
				for(int j=1;j<=n;j++)
					if(!dl[j])
						q.push((no){tp[j],j,lv[tp[j]]});
				no x=q.top();
				q.pop();
				if(x.le==i){
					if(la[x.l])
						tp[x.x]=la[x.l];
					else
						dl[x.x]=1;
				}
				else
					dl[x.x]=1;
				ans[i]=1;
			}
		}
		int ok=0;
		for(int i=ml+n+1;i;i--){
			if(ans[i])
				ok=1;
			if(ok)
				printf("%lld",ans[i]);
		}
	}
	else{
		for(int i=1;i<=cv;i++)
			rk[i]=cv-rk[i]+1;
		for(int i=1;i<=cv;i++)
			ss[rk[i]]=i;
		for(int i=1;i<=n;i++)
			for(int j=0;j<id[i].size();j++)
				if(j){
					int x=id[i][j];
					t.ad(1,1,cv,rk[x],rk[x],rk[x]+lv[x]-1);
				}
		for(int i=1;i<=n;i++)
			for(int j=0;j<id[i].size();j++)
				if(j&&!(vi[i][j]&&!nx[id[i][j]]))
					dd(rk[id[i][j]]);
		dfs(1e9);
		int ok=0;
		for(int i=ml+n+1;i;i--){
			if(ans[i])
				ok=1;
			if(ok)
				printf("%lld",ans[i]);
		}
	}
}
posted @ 2020-12-16 12:00  celerity1  阅读(372)  评论(0)    收藏  举报