cf#327 (Div. 1)E. Birthday(最长反链)

1 篇文章 0 订阅

链接:http://codeforces.com/contest/590/problem/E
题意:给出n(n<=750)个字符串,要求从中选出尽可能多的串,使得两两不是包含关系,并输出方案。(串长不超过 107

用AC自动机处理包含关系之后,题目转化为求最长反链,并输出方案。关于DAG的最长反链,可以参考这个,然而本题的难点在于输出方案。结论是:最小点覆盖的补集就是最长反链的方案。首先,由于最小点覆盖的补集大小=最长反链的大小,因此,只要证明这个补集是一个反链即可。而这是显然的,因为假如这不是一个反链,必然存在一对u,v,满足u,v不在点覆盖中,但是u,v之间有边,这就表明那并不是一个点覆盖,产生矛盾。
值得注意的是原有向无环图必须先求一边传递闭包,否则上述并不成立。
因此,只需要输出一种最小点覆盖的方案即可。自己yy了一种求点覆盖的方法:
1.先令左边的匹配的点都是点覆盖中的点
2.对于每个左边的非匹配点,假如他连接着一条未被覆盖的边,那么就把对应的右点置为覆盖点,同时,由于这个右点必然是一个匹配点,要将他匹配的点移出覆盖点
3.由于被移除了覆盖点,有些非匹配边可能未被覆盖到,因此这时候递归的进行2即可

#include<bits/stdc++.h>
using namespace std;
const int Maxn=10000009;
int n;
string ss[755];
int ch[Maxn][2];
int f[Maxn],last[Maxn];
int val[Maxn],id[755];
int cnt,tot;//zong jie dian,zong chuan
void insert(int idx,string &s)
{
    int rt=0;
    for(int i=0;i<s.size();i++)
    {
        int c=s[i]-'a';
        if(!ch[rt][c]){ch[rt][c]=cnt++;}
        rt=ch[rt][c];
    }
    if(!val[rt]){val[rt]=++tot;id[tot]=idx;}
}
bool G[755][755];
int linkl[755],linkr[755];
bool done[755],isl[755],isr[755];
bool dfs(int u)
{
    //printf("u=%d\n",u);
    for(int i=1;i<=tot;i++)
    {
        if(!G[u][i]||done[i])continue;
        done[i]=1;
        if(!linkr[i]||dfs(linkr[i]))
        {
            linkr[i]=u;
            return 1;
        }
    }
    return 0;
}
void getf()
{
    queue<int>q;
    f[0]=0;
    for(int c=0;c<2;c++)
    {
        int u=ch[0][c];
        if(u){f[u]=0;q.push(u);last[u]=0;}
    }
    while(!q.empty())
    {
        int r=q.front();q.pop();
        for(int c=0;c<2;c++)
        {
            int u=ch[r][c];
            if(!u)continue;
            q.push(u);
            int v=f[r];
            while(v&&!ch[v][c])v=f[v];
            f[u]=ch[v][c];
            last[u]=val[f[u]]?f[u]:last[f[u]];
        }
    }
}
void dfs2(int u)
{
    for(int i=1;i<=tot;i++)
    {
        if(!G[u][i])continue;
        if(!isr[i])
        {
            isr[i]=1;
            isl[linkr[i]]=0;
            dfs2(linkr[i]);
        }
    }
}
int main()
{
    scanf("%d",&n);
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        cin>>ss[i];
        insert(i,ss[i]);
    }
    getf();
    for(int i=1;i<=tot;i++)
    {
        int now=0;
        string &s=ss[id[i]];
        for(int j=0;j<s.size();j++)
        {
            int c=s[j]-'a';
            now=ch[now][c];
            if(j!=s.size()-1&&val[now])
            {
                G[i][val[now]]=1;
                continue;
            }
            if(val[last[now]])
            {
                G[i][val[last[now]]]=1;
            }
        }
    }
    for(int i=1;i<=tot;i++)
        for(int j=1;j<=tot;j++)
            for(int k=1;k<=tot;k++)
            {
                if(G[j][i]&&G[i][k])G[j][k]=1;
            }
    /*  
    for(int i=1;i<=tot;i++)
    {
        for(int j=1;j<=tot;j++)
            printf("%d ",G[i][j]);
        puts("");
    }
    */
    int ans=0;
    for(int i=1;i<=tot;i++)
    {
        memset(done,0,sizeof(done));
        //printf("i=%d\n",i);
        if(dfs(i))ans++;
    }
    //printf("ans=%d\n",ans);
    for(int i=1;i<=tot;i++)
        if(linkr[i])
            linkl[linkr[i]]=i;
    for(int i=1;i<=tot;i++)
        if(linkl[i])isl[i]=1;
    //for(int i=1;i<=tot;i++)printf("%d ",linkr[i]);puts("");
    for(int i=1;i<=tot;i++)
    {
        if(!linkl[i])
        {
            dfs2(i);
        }
    }
    vector<int>rep;
    for(int i=1;i<=tot;i++)if(!isl[i]&&!isr[i])rep.push_back(i);
    printf("%d\n",(int)rep.size());
    for(int i=0;i<rep.size();i++)
    printf("%d%c",rep[i],i==rep.size()?'\n':' ');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值