题目
题目链接:https://www.ybtoj.com.cn/contest/127/problem/1
\(n,\sum|S|\leq 5\times 10^5\)。
思路
由于每一个 ?
只能填 \(0\) 或 \(1\),不难想到 2-sat。
将串安装长度排序,把每一个 ?
分别当作 \(0\) 和 \(1\) 一次扔进一棵 Trie 中,Trie 上每一个节点维护以该节点结尾的字符串集合。走到一个点时将现在的字符串与该节点的集合中的字符串连边,注意一个字符串需要区分 ?
填 \(0\) 或 \(1\)。
然后跑 2-sat 即可。
但是这样的空间复杂度是 \(O(n^2)\) 的。不可以接受。我们发现如果空间需要卡满 \(O(n^2)\),那么短串一定多,这会导致答案为 NO。所以我们每插入 lim 个串就跑一次 tarjan,如果已经无解了就输出 NO 即可。我取 \(lim=1000\)。
说白了就是一个乱搞,实际上有时空严格 \(O(n+m)\) 的做法。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define NOT(x) ((x>m)?(x-m):(x+m))
using namespace std;
const int N=1000010,M=20000010;
int n,m,tot,cnt,head[N],dfn[N],low[N],col[N];
char s[N],t[N];
bool vis[N];
stack<int> st;
struct node
{
int pos,len,p;
}b[N];
struct edge
{
int next,to;
}e[M];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
bool cmp(node x,node y)
{
return x.len<y.len;
}
struct Trie
{
int tot,ch[N][2];
vector<int> end[N];
Trie() { tot=1; }
bool ins(int l,int r,int k)
{
int p=1;
for (int i=l;i<=r;i++)
{
int id=s[i]-48;
if (!ch[p][id]) ch[p][id]=++tot;
p=ch[p][id];
for (int j=0;j<end[p].size();j++)
add(end[p][j],NOT(k)),add(k,NOT(end[p][j]));
}
end[p].push_back(k);
return 0;
}
}trie;
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
st.push(x); vis[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if (vis[v])
low[x]=min(low[x],dfn[v]);
}
if (low[x]>=dfn[x])
{
int y; n++;
do {
y=st.top(); st.pop();
col[y]=n; vis[y]=0;
} while (x!=y);
}
}
int main()
{
freopen("code.in","r",stdin);
freopen("code.out","w",stdout);
// return printf("%d\n",sizeof(e)/1024/1024),0;
memset(head,-1,sizeof(head));
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
scanf("%s",t);
b[i]=(node){n+1,(int)strlen(t),0};
for (int j=0;j<b[i].len;j++)
{
s[++n]=t[j];
if (s[n]=='?') b[i].p=n;
}
}
sort(b+1,b+1+m,cmp);
for (int i=1;i<=m;i++)
{
if (b[i].p)
{
s[b[i].p]='0';
if (trie.ins(b[i].pos,b[i].pos+b[i].len-1,i))
return printf("NO"),0;
s[b[i].p]='1';
if (trie.ins(b[i].pos,b[i].pos+b[i].len-1,i+m))
return printf("NO"),0;
}
else
{
int id=i+(s[b[i].pos]-48)*m;
if (trie.ins(b[i].pos,b[i].pos+b[i].len-1,id))
return printf("NO"),0;
if (s[b[i].pos]==48) add(i+m,i);
if (s[b[i].pos]==49) add(i,i+m);
}
if (i%1000==0)
{
cnt=n=0;
for (int j=1;j<=i;j++)
if (!dfn[j]) tarjan(j);
for (int j=1;j<=i;j++)
if (col[j]==col[j+m])
return printf("NO"),0;
for (int j=1;j<=i;j++)
col[j]=dfn[j]=col[j+m]=dfn[j+m]=0;
}
}
cnt=n=0;
for (int i=1;i<=m;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=m;i++)
if (col[i]==col[i+m])
return printf("NO"),0;
printf("YES");
return 0;
}