正题
题目链接:https://loj.ac/p/3026
题目大意
给出nnn个点的一棵外向树,然后mmm个字符串和费用表示你每次可以花费这个费用覆盖路径字符串和给出字符串相等的路径,求覆盖所有边的最小花费(可以重复覆盖)
输出方案
1≤n≤500,1≤m≤105,∑∣S∣≤1061\leq n\leq 500,1\leq m\leq10^5,\sum |S|\leq 10^61≤n≤500,1≤m≤105,∑∣S∣≤106
解题思路
注意到nnn很小,可以考虑枚举计算所有路径的最小花费,先构一个TrieTrieTrie即可处理这部分。
然后考虑怎么覆盖的问题,和[NOI2008] 志愿者招募类似的,我们可以通过边调走流来实现覆盖问题。
先原点向每一个叶子连流量为1的边,设sizxsiz_xsizx表示xxx的子树中有多少个叶子那么xxx向faxfa_xfax连接流量为sizx−1siz_x-1sizx−1的边,然后根向汇点连流量为siz1siz_1siz1的边。
这样就保证了每条边都至少有一个流量被调走,但是需要考虑单链上的重复覆盖所以还需要每个点向儿子连无向流量的边。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const ll N=510,M=1e6+10;
struct node{ll to,next,w;
}a[N];
ll n,m,t,tot,ans,MF,ls[N],siz[N],fa[N];
ll cnt,ch[M][26],cost[M],fail[M],mark[M];
queue<int> q;char s[M];
void addl(ll x,ll y,ll w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void Insert(char *s,ll val,ll num){ll x=1,m=strlen(s);for(ll i=0;i<m;i++){ll c=s[i]-'a';if(!ch[x][c])ch[x][c]=++cnt;x=ch[x][c];}if(cost[x]>val)cost[x]=val,mark[x]=num;return;
}
void GetFail(){for(ll i=0;i<26;i++)q.push(ch[1][i]);while(!q.empty()){ll x=q.front();for(ll i=0;i<26;i++){if(!ch[x][i])ch[x][i]=ch[fail[x]][i];else{fail[ch[x][i]]=ch[fail[x]][i];q.push(ch[x][i]);}}}return;
}
namespace NF{struct node{ll to,next,w,c,typ;}a[N*N];ll tot=1,s,t,ls[N],f[N],mf[N],pre[N];bool v[N];void addl(ll x,ll y,ll w,ll c,ll mk){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[tot].c=c;//a[tot].typ=mk;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;a[tot].c=-c;a[tot].typ=mk;return;}bool SPFA(){memset(f,0x3f,sizeof(f));q.push(s);v[s]=1;f[s]=0;mf[s]=f[0];mf[t]=0;while(!q.empty()){ll x=q.front();v[x]=0;q.pop();for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(a[i].w&&f[x]+a[i].c<f[y]){f[y]=f[x]+a[i].c;pre[y]=i;mf[y]=min(mf[x],a[i].w);if(!v[y])v[y]=1,q.push(y);}}}return mf[t];}void Updata(){ll x=t;ans+=mf[t]*f[t];MF+=mf[t];while(x!=s){a[pre[x]].w-=mf[t];a[pre[x]^1].w+=mf[t];x=a[pre[x]^1].to;}return;}void GetAns(){while(SPFA())Updata();return;}void Print(){ll sum=0;for(ll i=2;i<=tot;i++)if(a[i].typ)sum+=a[i].w;printf("%lld\n",sum);for(ll i=2;i<=tot;i++)if(a[i].typ){while(a[i].w)printf("%lld %lld %lld\n",a[i^1].to,a[i].to,a[i].typ),a[i].w--;}}
}
void calc(ll x,ll p,ll s){if(!p)return;if(cost[p]<=1e9)NF::addl(x,s,n,cost[p],mark[p]);for(ll i=ls[x];i;i=a[i].next)calc(a[i].to,ch[p][a[i].w],s);return;
}
void solve(ll x){siz[x]+=(!ls[x]);if(!ls[x])NF::addl(NF::s,x,1,0,0);calc(x,1,x);for(ll i=ls[x];i;i=a[i].next){NF::addl(x,a[i].to,n,0,0);solve(a[i].to),siz[x]+=siz[a[i].to];}if(fa[x]&&siz[x]>1)NF::addl(x,fa[x],siz[x]-1,0,0);return;
}
signed main()
{memset(cost,0x3f,sizeof(cost));scanf("%lld%lld%lld",&n,&m,&t);for(ll i=2;i<=n;i++){ll x;char op[3];scanf("%lld%s",&fa[i],op);addl(fa[i],i,op[0]-'a');}cnt=1;for(ll i=1;i<=m;i++){ll c;scanf("%lld%s",&c,s);Insert(s,c,i);}NF::s=n+1;NF::t=n+2;solve(1);NF::addl(1,NF::t,siz[1],0,0);NF::GetAns();if(MF!=siz[1])return puts("-1")&0;printf("%lld\n",ans);if(t)NF::Print();return 0;
}