[JZOJ5525] Atom

Description

这里写图片描述
这里写图片描述

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");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值