cf#327 (Div. 1)E. Birthday(最長反鏈)

鏈接: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':' ');
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章