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