DAG最小可重路径覆盖or最长反链的一种做法

zrO xtx Orz

感觉就是抄了一遍https://zybuluo.com/AntiLeaf/note/804022
不得不说,这个做法非常强。
由于博主水平有限,有可能有一些纯感性理解的东西.

首先,最长反链这个说法似乎有一些问题,这里的反链是指互不包含的点集。
最小可重路径覆盖指边和点都可以重复.
最小不可重路径覆盖指点不重复.

这样根据dilworth定理,这个东西对应最小链剖分,也就是求出传递闭包并连边后的DAG的最小不可重路径覆盖。

最小不可重路径覆盖还是比较简单的.
他有一个经典做法,拆点建二分图,原点向左边的点连边,容量为1,左边的点向右边的点连原图的边,容量为1,右边的点向汇点连边,容量为1.
考虑一个匹配的含义就是原来的\(n\)个路径中的两个结合了.
那么答案就是点数减最大匹配数.

最小可重路径覆盖,第一种做法是求出传递闭包,然后转化为不可重,但是点数较多时并不能求出传递闭包.
另一种做法考虑优化建图,原来的边的含义是直接或间接到达,现在的边只有直接到达的含义,那么为了可以让间接到达存在,每个点的入点向出点连Inf边,并且原图的边连Inf.

如果这个问题的最长反链是一个点集的呢?
只需要源点只向该点集连边,只有该点集向汇点连边即可,因为考虑其他的边和点都是维护是否可达关系的辅助边和点.

一个用到这个的题的代码

#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;
}
posted @ 2019-04-01 10:05  Yuhuger  阅读(715)  评论(0编辑  收藏  举报