DAG最小可重路徑覆蓋or最長反鏈的一種作法

#zrO xtx Orz 感受就是抄了一遍https://zybuluo.com/AntiLeaf/note/804022 不得不說,這個作法很是強。 因爲博主水平有限,有可能有一些純感性理解的東西.c++

首先,最長反鏈這個說法彷佛有一些問題,這裏的反鏈是指互不包含的點集。 最小可重路徑覆蓋指邊和點均可以重複. 最小不可重路徑覆蓋指點不重複.閉包

這樣根據dilworth定理,這個東西對應最小鏈剖分,也就是求出傳遞閉包並連邊後的DAG的最小不可重路徑覆蓋。優化

最小不可重路徑覆蓋仍是比較簡單的. 他有一個經典作法,拆點建二分圖,原點向左邊的點連邊,容量爲1,左邊的點向右邊的點連原圖的邊,容量爲1,右邊的點向匯點連邊,容量爲1. 考慮一個匹配的含義就是原來的$n$個路徑中的兩個結合了. 那麼答案就是點數減最大匹配數.ui

最小可重路徑覆蓋,第一種作法是求出傳遞閉包,而後轉化爲不可重,可是點數較多時並不能求出傳遞閉包. 另外一種作法考慮優化建圖,原來的邊的含義是直接或間接到達,如今的邊只有直接到達的含義,那麼爲了可讓間接到達存在,每一個點的入點向出點連Inf邊,而且原圖的邊連Inf.url

若是這個問題的最長反鏈是一個點集的呢? 只須要源點只向該點集連邊,只有該點集向匯點連邊便可,由於考慮其餘的邊和點都是維護是否可達關係的輔助邊和點.spa

一個用到這個的題的代碼.net

#include <bits/stdc++.h>
  
using namespace std;
const int N=100005;
char ss[N];
int fa[N];
int len[N];
int trans[N][26],la,tot=1;
int s,t;
int aa[N];
int val[N];
int getfail(char *s,int x,int r){
    while (s[r]!=s[r-len[x]-1]){
        x=fa[x];
        //cerr<<x<<" "<<len[x]<<endl;
    }
    return x;
}
  
void init(){
    len[0]=0;
    len[1]=-1;
    fa[0]=1;
}
  
void build(char *s,int l,int r){
    //cerr<<"BBBB"<<" "<<la<<endl;
    char c=s[r]-'a';
    int cur=getfail(s,la,r);
    //cerr<<"cur"<<cur<<endl;
    if (!trans[cur][c]){
        ++tot;
        len[tot]=len[cur]+2;
        fa[tot]=trans[getfail(s,fa[cur],r)][c];
        //cerr<<"tot"<<tot<<" "<<fa[tot]<<" "<<cur<<endl;
        trans[cur][c]=tot;
    }
    la=trans[cur][c];
    val[la]=max(val[la],aa[r]);
    //cerr<<"build"<<l<<" "<<r<<endl;
}
  
struct edge{
    int y,cap,op;
};
int pp=0;
vector<edge> g[N+N];
void add(int x,int y,int z){
    //cerr<<"add"<<x<<" "<<y<<" "<<z<<" "<<++pp<<endl;
    g[x].push_back({y,z,g[y].size()});
    g[y].push_back({x,0,g[x].size()-1});
}
  
int cur[N+N],d[N+N];
bool bfs(){
    queue<int> q;
    q.push(s);
    for (int i=s; i<=t; ++i) cur[i]=d[i]=0;
    d[s]=233;
    while (!q.empty()){
        int x=q.front(); q.pop();
        //cerr<<"XXXXXXXXXXXX"<<x<<" "<<g[x].size()<<endl;
        for (auto j:g[x]){
            //cerr<<"cap"<<j.cap<<endl;
            if (j.cap&&!d[j.y]){
                //cerr<<j.y<<endl;
                d[j.y]=d[x]+1;
                q.push(j.y);
            }
        }
    }
    //cerr<<d[t]<<endl;
    return d[t];
}
  
int dfs(int x,int fl){
    if (x==t) return fl;
    //cerr<<"dfs"<<x<<" "<<fl<<endl;
    int orz=fl;
    for (int &j=cur[x]; j<g[x].size(); ++j){
        edge &e=g[x][j];
        int t=0;
        if (e.cap&&d[e.y]==d[x]+1&&(t=dfs(e.y,min(fl,e.cap)))){
            //cerr<<"init"<<endl;
            e.cap-=t;
            g[e.y][e.op].cap+=t;
            fl-=t;
            if (!fl) return orz;
        }
    }
    return orz-fl;
}
int maxflow(){
    int ans=0;
    while (bfs()){
        //cerr<<"BFS"<<endl;
        //for (int i=s; i<=t; ++i) cerr<<d[i]<<" ";
        //cerr<<endl;
        //getchar();
        ans+=dfs(s,2333333);
    }
    //cerr<<"ans"<<ans<<endl;
    return ans;
}
  
  
int calc(int lim){
    //cerr<<"calc"<<lim<<endl;
    s=0;
    t=tot*2+1;
    int bbbb=0;
    for (int i=s; i<=t; ++i) g[i].clear();
    for (int i=2; i<=tot; ++i){
        //cerr<<"IIIII"<<i<<endl;
        if (val[i]>=lim){
            ++bbbb;
            add(s,i*2-1,1);
            add(i*2,t,1);
        }
        add(i*2,i*2-1,23333333);
    }
    //cerr<<"___________"<<endl;
    //cerr<<"tot"<<tot<<endl;
    for (int i=2; i<=tot; ++i)
        for (int c=0; c<26; ++c){
        if (trans[i][c]){
            add(i*2-1,trans[i][c]*2,233333);
        }
    }
    for (int i=2; i<=tot; ++i)
        if (fa[i]>1) add(fa[i]*2-1,i*2,2333333);
  
  
      
    return bbbb-maxflow();
}
  
void update(){
    for (int i=tot; i>=2; --i) val[fa[i]]=max(val[fa[i]],val[i]);
}
  
int n,k;
int main(){
    scanf("%d%d",&n,&k);
    scanf("%s",ss+1);
    vector<int> v;
    for (int i=1; i<=n; ++i) scanf("%d",&aa[i]),v.push_back(aa[i]);
    //cerr<<"!!!!!"<<endl;
    init();
    for (int i=1; i<=n; ++i) build(ss,1,i);
    //  for (int i=1; i<=tot; ++i) cerr<<val[i]<<" ";
    update();
    //cerr<<"????"<<endl;
    //cerr<<"tpt:"<<tot<<endl;
    sort(v.begin(),v.end());
    //for (int i=1; i<=tot; ++i) cerr<<fa[i]<<" ";
    //cerr<<endl;
    //for (int i=1; i<=tot; ++i) cerr<<val[i]<<" ";
    //calc(3);
    //return 0;
    int ret=-1;
    for (int l=0,r=v.size()-1,mid=(l+r)>>1; l<=r; mid=(l+r)>>1)
        if (calc(v[mid])>=k) ret=v[mid],l=mid+1; else r=mid-1;
    if (ret==-1) cout<<"NEGATIVE"<<endl;
    else cout<<ret<<endl;
}
相關文章
相關標籤/搜索