zoukankan      html  css  js  c++  java
  • 【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
    ",ans);
    	for (int i=1;i<=n;i++)
    		if (flag[0][i] && !flag[1][i]) printf("%d ",i);
    	return 0;
    }
    
  • 相关阅读:
    怎么制作html5网站页面让它适应电脑和手机的尺寸
    js面向对象 下
    认识面向对象及代码示例
    Math 对象
    js事件驱动函数
    模拟js中注册表单验证
    敏感词过滤 简单 模仿
    模仿随机验证码-简单效果
    字符串方法(函数)
    js中字符串概念
  • 原文地址:https://www.cnblogs.com/stoorz/p/14453123.html
Copyright © 2011-2022 走看看