Description

[JZOJ5525] Atom_网络流


[JZOJ5525] Atom_图论_02

Solution

可以先把回文树构出来

那么一个节点的val就可以很容易在fail树上递推一下得到

根据回文树的性质,我们发现对于某一个节点所代表的字符串是另一个节点所代表的字符串的子串,当且仅当一个节点能通过fail指针和父边(都是有向的)到达另一个节点

可以二分答案,只有比答案大的点才能选

问题就转化为在一个有向无环图中,选最多的点,使得任意两个选的点一个不能走到另一个。

这东西有个学名叫最长反链

链是任意两个点都有其中一个能走到另一个

反链就是任意两个点都不满足

又有Dilworth定理:DAG的最长反链=最小链覆盖

对于最小链覆盖,可以将一个点X拆成X和X’,当存在边(X,Y)时,将X向Y’连边,这是一个二分图,最小链覆盖数就是n(拆点之前)-最大匹配数

然而我们发现这题二分答案以后有的点是不能选的
这样就不满足二分图的性质了

考虑改用网络流
能选的点 源点向它连一条容量为1,它向汇点连容量为1

不能选就不连
对于原图的边(X,Y),X向Y’连容量为1的边的同时,X’向Y’连容量为正无穷的边

答案就是(能选的点数)-最大流

X’向Y’的边,实际上保证了流量仍然能通过这个点传递出去,否则如果这个点不能选,它就不能向汇点传递了

用Dinic跑的比较快

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define INF 1802201963
using namespace std;
int fail[N],t[N][26],m1,n1,even,odd,len[N],n,m,val[N],st,ed,pr[N],nt[2*N],dt[2*N],fs[N],fv[2*N],fx[2*N],n2,lw[N],rw[N],h[N],d[N];
char ch[N];
void prp()
{
even=2,odd=1,len[2]=0,len[1]=-1,n1=2;
int ls=2;
fail[2]=1;
fo(i,1,n)
{
int p=ls,c=ch[i]-'a';
while(p>1&&ch[i-len[p]-1]!=ch[i]) p=fail[p];
if(!t[p][c])
{
t[p][c]=++n1,len[n1]=len[p]+2,ls=n1;
if(p==1) fail[ls]=2;
else
{
p=fail[p];
while(p>1&&!t[p][c]) p=fail[p];
if(!t[p][c]) fail[ls]=2;
else fail[ls]=t[p][c];
}
}
else ls=t[p][c];
pr[ls]=max(pr[ls],val[i]);
}
}
void dfs(int k)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
dfs(p);
pr[k]=max(pr[k],pr[p]);
}
}
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
fv[m1]=z;
}
void hb(int x,int y,int z)
{
link(x,y,z),link(y,x,0),fx[m1]=m1-1,fx[m1-1]=m1;
}
bool bfs()
{
int l=0,r=1;
memset(h,0,sizeof(h));
d[1]=st;
h[st]=1;
while(l<r)
{
int k=d[++l];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(fv[i]>0&&h[p]==0) h[p]=h[k]+1,d[++r]=p;
}
}
return (h[ed]>0);
}
int dinic(int k,int s)
{
if(k==ed) return s;
int sv=0;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(h[p]==h[k]+1&&fv[i])
{
int v=dinic(p,min(s,fv[i]));
if(v)
{
sv+=v,s-=v;
fv[fx[i]]+=v,fv[i]-=v;
if(!s) break;
}
}
}
if(sv==0) h[k]=-1;
return sv;
}
bool pd(int lim)
{
m1=0;
memset(nt,0,sizeof(nt));
memset(fs,0,sizeof(fs));
fo(i,2,n1) hb(lw[fail[i]],rw[i],1),hb(rw[fail[i]],rw[i],INF);
fo(i,1,n1)
fo(j,0,25)
if(t[i][j]) hb(lw[i],rw[t[i][j]],1),hb(rw[i],rw[t[i][j]],INF);
int v=0;
fo(i,1,n1)
if(pr[i]>=lim) hb(st,lw[i],1),hb(rw[i],ed,1),v++;
while(bfs())
v-=dinic(st,INF);
return (v>=m);
}
int main()
{
cin>>n>>m;
scanf("\n%s",ch+1);
fo(i,1,n) scanf("%d",&val[i]);
prp();
fo(i,1,n1) if(fail[i]!=0) link(fail[i],i,0);
dfs(1);
memset(fs,0,sizeof(fs));
memset(nt,0,sizeof(nt));
n2=0;
fo(i,1,n1) lw[i]=++n2,rw[i]=++n2;
st=++n2,ed=++n2;
int l=0,r=100000;
pd(67746);
while(l+1<r)
{
int mid=(l+r)/2;
if(pd(mid)) l=mid;
else r=mid;
}
if(pd(r)) printf("%d\n",r);
else if(pd(l)) printf("%d\n",l);
else printf("NEGATIVE\n");
}