Codeforces 590 E :Birthday(最长反链)

传送门

题解:
依照题意找最长反链即可。

不过要输出方案,我们可以这么做:
1.传递闭包,二分图匹配。
2.两边都在二分图最大独立集中的点即为最长反链。

证明:
1.这个方案肯定是反链。
2.设最大匹配为 M ,独立集大小为 2 N M ,最小链覆盖为 N M ,依据抽屉原题,反链大小 N M ,而“只存在于独立集一边”的点 N ,所以这个方案求出来的点 N M , 所以这是最长反链。

实际上, Dilworth定理也可以这么证明。

怎么找最大独立集呢,首先不在最大匹配中的点在独立集中。我们从一侧未匹配点开始dfs,另一侧要求只能走匹配路径。 我们可以发现这样之后,这一侧的访问到的点和另一侧未访问的点形成最大独立集。

论文哥给的证明:
显然不存在 这一侧访问 另一侧未访问 的边,所以是个独立集。这一侧未访问的点一定是匹配了的(因为是从所有未匹配点开始dfs),剩下的匹配边这一侧被访问了,所以另一侧也应该是被访问的,而且另一侧被访问的点一定是匹配了的(否则就找到了增广路),所以这一侧未访问的点和另一侧访问的点数=最大匹配数(实际上就是最小点覆盖)。那么这个独立集大小就是点数-匹配数,显然不可能更大。

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

const int N=755, M=2e7+5;
int n;
string s[N];
int son[M][2],fail[M],id[M],tot=1;
int que[M],hd,tl,MM,vis[N*2],vs;
int G[N][N],mate[N*2];
vector <int> edge[N*2];
inline bool dfs(int x) {
    vis[x]=vs;
    for(auto i:edge[x])
        if(!mate[i] || (vis[mate[i]]!=vs && dfs(mate[i]))) 
            return mate[i]=x, mate[x]=i, true;
    return false;
}
inline void dfs2(int x) {
    vis[x]=vs;
    for(auto i:edge[x]) 
        if(mate[i] && vis[i]!=vs) vis[i]=vs, dfs2(mate[i]);
}
int main() {
    cin>>n; MM=n; son[0][1]=son[0][0]=1;
    for(int i=1;i<=n;i++) {
        cin>>s[i]; int p=1;
        for(int j=0;j<s[i].length();++j) {
            if(!son[p][s[i][j]-'a']) son[p][s[i][j]-'a']=++tot;
            p=son[p][s[i][j]-'a']; 
        } id[p]=i;
    } 
    que[hd=tl=1]=1;
    while(hd<=tl) {
        int u=que[hd++];
        for(int j=0;j<2;++j) {
            int v=fail[u];
            while(!son[v][j]) v=fail[v];
            if(son[u][j]) fail[son[u][j]]=son[v][j], que[++tl]=son[u][j];
            else son[u][j]=son[v][j];
        }
    }
    for(int i=1;i<=tl;i++) {
        int u=que[i];
        if(!id[u]) id[u]=id[fail[u]];
    }
    for(int i=1;i<=n;i++) {
        int p=1;
        for(int j=0;j<s[i].length();++j) {
            p=son[p][s[i][j]-'a'];
            if(j==s[i].length()-1) G[i][id[fail[p]]]=1;
            else G[i][id[p]]=1;
        }
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                G[i][j]|=(G[i][k]&G[k][j]);

    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(G[i][j]) edge[i].push_back(j+n), edge[j+n].push_back(i);

    for(int i=1;i<=n;i++)
        if(!mate[i]) if(++vs,dfs(i)) --MM;
    cout<<MM<<'\n'; ++vs;
    for(int i=1;i<=n;i++)
        if(!mate[i]) dfs2(i);
    for(int i=1;i<=n;i++)
        if(vis[i]==vs && vis[i+n]!=vs) cout<<i<<' ';
}

猜你喜欢

转载自blog.csdn.net/qq_35649707/article/details/80973513