fjwc2019 D1T3 不同的缩写(dinic+trie+dfs)

Posted kafuuchino

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fjwc2019 D1T3 不同的缩写(dinic+trie+dfs)相关的知识,希望对你有一定的参考价值。

#180. 「2019冬令营提高组」不同的缩写

乍看之下没有什么好的方法鸭.......于是考虑暴力。

长度?二分似乎可行。

于是我们二分最长子串的长度(设为$len$),蓝后暴力查找。

先在每个串内练好后继边建图

for(int i=1;i<=n;++i){
        int len=strlen(a[i]+1);
        for(int j=0;j<26;++j) To[i][len][j]=-1;
        for(int j=len-1;j>=0;--j){
            memcpy(To[i][j],To[i][j+1],sizeof(To[i][j+1]));
            To[i][j][a[i][j+1]-a]=j+1;
        }
    }

每次用dfs查找一个串中长度不超过$len$的子串个数。

一个重要的剪枝:当长度不超过$len$的子串个数已经超过n个时,显然我们可以停止查找。因为显然无论怎么配,这个串都有解。

现在,我们筛掉了一定有解的串,那么对于剩下的串我们怎么判断答案为$len$时是否有解?

考虑暴力建出一棵trie树,保存剩下串的信息。

接下来我们实现匹配

首先我们先新建总起点,终点$S,T$;以及$n-w$个点表示对应的子串(前面剪枝剪掉的子串(设为$w$个)就不用建点辣)

(当然你可以方便地直接开n个点)

我们遍历子串$k$,当访问到树上的某个节点$p$时,从$k$向$p$连一条流量为1的边(没错!我们等下要跑网络流),表示一种匹配。

所有串遍历完后,trie上的每个点都向$T$连一条流量为1的边。

最后,$S$向$n-w$个代表子串的点连一条流量为1的边。

于是我们就可以$S /rightarrow T$愉快地跑一遍dinic辣


 

找到了最短的长度,现在考虑输出其中一种方案。

直接上dfs遍历每一个串,如果找到一个还未打上结束标记的节点,就把这个节点的结束标记打上该串的编号,然后跳出dfs,遍历下个串。

最后开个字符栈,再跑遍dfs。

蓝后就是艰难(for me)的打code了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define stop system("pause")
using namespace std;
inline int min(int a,int b){return a<b?a:b;}
const int inf=2147483647;
#define N 305
#define M 500005
char a[N][N]; bool vis[M];
int n,ans=-1,To[N][N][27],tot,is[N];
int d[M],cur[M],S,T;
int tri,p[M][27],fr[M];
int cnt,hd[M],nxt[M],ed[M],poi[M],cap[M];
queue <int> h;
string b,name[N];
void clear(int x){for(int i=0;i<26;++i)p[x][i]=0;}
void add(int x,int y,int v){
    nxt[ed[x]]=++cnt; hd[x]=hd[x]?hd[x]:cnt;
    ed[x]=cnt; poi[cnt]=y; cap[cnt]=v;
}
void ins(int x,int y,int v){add(x,y,v);add(y,x,0);}
#define to poi[i]
bool bfs(){
    memset(vis,0,sizeof(vis));
    memset(d,-1,sizeof(d));
    h.push(S); vis[S]=1; d[S]=0;
    while(!h.empty()){
        int x=h.front(); h.pop();
        for(int i=hd[x];i;i=nxt[i]){
            if(!vis[to]&&cap[i]>0){
                vis[to]=1;
                d[to]=d[x]+1;
                h.push(to);
            }
        }
    }return vis[T];
}
int dfs(int x,int a){//dinic
    if(x==T||a==0) return a;
    int F=0,f;
    for(int &i=cur[x];i&&a>0;i=nxt[i])
        if(d[to]==d[x]+1&&(f=dfs(to,min(cap[i],a)))>0)
            cap[i]-=f,a-=f,cap[i^1]+=f,F+=f;
    return F;
}
int dinic(){
    int re=0;
    while(bfs()){
        for(int i=1;i<=tri+2;++i) cur[i]=hd[i];
        re+=dfs(S,inf);
    }
    return re;
}
void dfs1(int d,int id,int o,int dl){
    if(d) ++tot;
    if(d==dl||tot>=n) return;
    for(int i=0;i<26&&tot<n;++i)
        if(To[id][o][i]!=-1)
            dfs1(d+1,id,To[id][o][i],dl);
}
void dfs2(int d,int id,int o,int dl,int u){
    if(d) ins(id,u,1);
    if(d==dl) return;
    for(int i=0;i<26;++i)
        if(To[id][o][i]!=-1){
            if(!p[u][i]) p[u][i]=++tri,clear(tri);
            dfs2(d+1,id,To[id][o][i],dl,p[u][i]);
        }
}
bool chk(int lim){
    memset(ed,0,sizeof(ed));
    memset(hd,0,sizeof(hd));
    memset(nxt,0,sizeof(nxt));
    cnt=1; tri=n+1; clear(n+1);
    for(int i=1;i<=n;++i){
        tot=0; is[i]=0;
        dfs1(0,i,0,lim);
        if(tot<n) is[i]=1,dfs2(0,i,0,lim,n+1);//剪枝
    }S=tri+1;T=tri+2;
    int tflow=0;
    for(int i=1;i<=n;++i)
        if(is[i]) ++tflow,ins(S,i,1);
    for(int i=n+1;i<=tri;++i) ins(i,T,1);
    return tflow==dinic();
}
int dfs3(int d,int id,int o,int dl,int u){
    if(d&&!fr[u]){fr[u]=id; return 1;}
    if(d==dl) return 0;
    for(int i=0;i<26;++i)
        if(To[id][o][i]!=-1){
            if(!p[u][i]) p[u][i]=++tri,clear(tri);
            if(dfs3(d+1,id,To[id][o][i],dl,p[u][i]))
                return 1;
        }
    return 0;
}
void dfs4(int u){
    if(fr[u]) name[fr[u]]=b;
    for(int i=0;i<26;++i)
        if(p[u][i]){
            b+=(i+a); dfs4(p[u][i]);
            b.erase(b.size()-1);
        }
}
int main(){
    freopen("diff.in","r",stdin);
    freopen("diff.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%s",a[i]+1);
    for(int i=1;i<=n;++i){
        int len=strlen(a[i]+1);
        for(int j=0;j<26;++j) To[i][len][j]=-1;
        for(int j=len-1;j>=0;--j){
            memcpy(To[i][j],To[i][j+1],sizeof(To[i][j+1]));
            To[i][j][a[i][j+1]-a]=j+1;
        }
    }
    int l=1,r=n;
    while(l<r){
        int mid=(l+r)/2;
        if(chk(mid)) r=mid;
        else l=mid+1;
    }if(!chk(l)){printf("-1");return 0;}
    ans=l;
    for(int i=1;i<=n;++i)
        if(is[i])
            for(int j=hd[i];j;j=nxt[j])
                if(cap[j]==0)
                    fr[poi[j]]=i;
    for(int i=1;i<=n;++i)
        if(!is[i]) dfs3(0,i,0,ans,n+1);
    b=""; dfs4(n+1);
    printf("%d
",ans);
    for(int i=1;i<=n;++i) cout<<name[i]<<endl;
    return 0;
}

 

以上是关于fjwc2019 D1T3 不同的缩写(dinic+trie+dfs)的主要内容,如果未能解决你的问题,请参考以下文章

fjwc2019 D2T2 定价 (栈+set+贪心)

fjwc2019 D1T1 (dp+树状数组)

fjwc2019 D3T1 签到题 (贪心)

fjwc2019 D6T1 堆(组合数+打表)

fjwc2019 D6T2 密文(trie+贪心)

FJWC2020 题解合(gu)集(gu)