无进位加法
先考虑一个多项式做法:
根据套路从高到低位确定答案。
令当前位\(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]);
}
}
}