zoukankan      html  css  js  c++  java
  • 无进位加法

    先考虑一个多项式做法:
    根据套路从高到低位确定答案。
    令当前位(i)(0),贪心的让后面合法。显然把(1~i-1)位都设成(1)进行判定。
    考虑当前(a)的最大值(v),扫到第(j)位。
    如果(v)的长度大于(j)显然无解
    否则如果(v)的第(i)位为(0),则删除最大值继续判定。
    如果(v)的第(i)为为(1),则把这一位的(1)和前导(0)删除后继续判定
    取出最大值的正确性可以感性理解,就是如果当前位不覆盖最大值,则后面的位更难覆盖。
    考虑加速比较的过程。
    我们需要支持一个数据结构,要求快速插入,删除最大值,取出最大值。
    使用堆+二分+哈希可以做到(O(n^2log_2^2n))
    但是我们比较只涉及两个开头为(1)的某个字符串的后缀(还要把前面补(0))比较。
    使用基数排序/二分+哈希即可将所有后缀排序。
    比较时间复杂度降低至(O(n^2log_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]);
    		}
    	}
    }
    
  • 相关阅读:
    文件操作函数
    sublime text2 常用快捷键
    过滤掉字段为空的数据
    PHP安装memcache扩展接口步骤
    大数据的逻辑,其实是中医的逻辑
    大数据的逻辑,其实是中医的逻辑
    SPSS输出结果如何在word中设置小数点前面显示加0
    SPSS输出结果如何在word中设置小数点前面显示加0
    掌握4个有效的数据分析要点,切实解决用户痛点
    掌握4个有效的数据分析要点,切实解决用户痛点
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14143164.html
Copyright © 2011-2022 走看看