【CF590E】Birthday

题目

题目链接:https://codeforces.com/problemset/problem/590/E
给定 \(n\) 个仅包含 a,b 的字符串,保证它们两两不同。你需要去掉尽可能少的字符串,使得剩下的字符串中不存在某一个串是另一个串的子串。
\(n \le 750\)\(\sum_{i=1}^n |s_i| \le 10^7\)

思路

首先建出 AC 自动机,然后把每一个串都扔到上面跑匹配。注意如果每次暴力跳 fail 显然会 T。所以我们在跳 fail 的同时路径压缩一下,建出一张 DAG。
然后我们需要求的就是这张 DAG 的最长反链,可以证明一张 DAG 的最长反链等于它的最小可重链覆盖。先 Floyd 传递闭包,然后把每一个点拆成两个点 \(x_1,x_2\),如果 DAG 上 \(x\) 可以到 \(y\),那么从 \(x_1\)\(x_2\) 连边。
因为二分图最小可重链覆盖等于 \(n-\) 二分图最大匹配,所以我们先跑最大匹配即可得到最长反链大小。
然后考虑求出方案。我们先求出这张二分图的最小点覆盖,具体的,从每一个 \(x_2\) 开始,右边往左边只能走非匹配边,左边往右边只能走匹配边,把中途所有经过的点标记,这样右边没被标记的点和左边被标记的点的集合就是最小点覆盖。因为右边所有未匹配的点一定会被标记,右边匹配的点如果没有标记,左边的匹配点就一定会被标记。
然后最小点覆盖和最大独立集互为补集,所以这样右边被标记且左边没被标记的点就是最大独立集中的点了。
如果一个点拆出的两个点都在最大独立集中,那么这个点就是最长反链中的点。
时间复杂度 \(O(n^3+\sum m)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=760,M=10000010;
int n,ans,tot,vis[N],head[N],pos[N],link[2][N];
bool dis[N][N],flag[2][N];
char s[M];

struct ACA
{
	int tot,ch[M][3],fail[M],end[M],fa[M];
	ACA() { tot=1; }
	
	int ins(int i,char *s)
	{
		int len=strlen(s+1),p=1;
		for (int j=1;j<=len;j++)
		{
			int id=s[j]-'a'+1;
			if (!ch[p][id]) ch[p][id]=++tot,fa[tot]=p;
			p=ch[p][id];
		}
		end[p]=i;
		return p;
	}
	
	void build()
	{
		queue<int> q;
		if (ch[1][1]) q.push(ch[1][1]),fail[ch[1][1]]=1;
		if (ch[1][2]) q.push(ch[1][2]),fail[ch[1][2]]=1;
		while (q.size())
		{
			int u=q.front(); q.pop();
			if (ch[u][1]) fail[ch[u][1]]=ch[fail[u]][1],q.push(ch[u][1]);
				else ch[u][1]=ch[fail[u]][1];
			if (ch[u][2]) fail[ch[u][2]]=ch[fail[u]][2],q.push(ch[u][2]);
				else ch[u][2]=ch[fail[u]][2];
		}
	}
	
	void find(int i)
	{
		for (int j=pos[i];j>1;j=fa[j])
		{
			int x=fail[j],y=fail[j];
			while (!end[x] && x>1) x=fail[x];
			for (int z;y!=x;y=z)
				z=fail[y],fail[y]=x;
			fail[j]=x;
			if (end[j] && end[j]!=i) dis[i][end[j]]=1;
			if (x>1) dis[i][end[x]]=1;
		}
	}
}AC;

bool find(int x,int tag)
{
	if (vis[x]==tag) return 0;
	vis[x]=tag;
	for (int v=1;v<=n;v++)
		if (dis[x][v] && (!link[0][v] || find(link[0][v],tag)))
		{
			link[0][v]=x; link[1][x]=v;
			return 1;
		}
	return 0;
}

void dfs(int x)
{
	if (flag[0][x]) return;
	flag[0][x]=1;
	for (int v=1;v<=n;v++)
		if (dis[x][v] && !flag[1][v])
		{
			flag[1][v]=1;
			dfs(link[0][v]);
		}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		pos[i]=AC.ins(i,s);
	}
	AC.build();
	for (int i=1;i<=n;i++)
		AC.find(i);
	for (int k=1;k<=n;k++)
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				if (i!=j && j!=k && i!=k)
					dis[i][j]|=(dis[i][k]&dis[k][j]);
	ans=n;
	for (int i=1;i<=n;i++)
		if (!vis[i]) ans-=find(i,i);
	for (int i=1;i<=n;i++)
		if (!link[1][i]) dfs(i);
	printf("%d\n",ans);
	for (int i=1;i<=n;i++)
		if (flag[0][i] && !flag[1][i]) printf("%d ",i);
	return 0;
}
posted @ 2021-02-26 16:54  stoorz  阅读(138)  评论(0编辑  收藏  举报