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;
}