Loj#3026-「ROIR 2018 Day1」管道监控【Trie,费用流】

正题

题目链接:https://loj.ac/p/3026


题目大意

给出(n)个点的一棵外向树,然后(m)个字符串和费用表示你每次可以花费这个费用覆盖路径字符串和给出字符串相等的路径,求覆盖所有边的最小花费(可以重复覆盖)

输出方案

(1leq nleq 500,1leq mleq10^5,sum |S|leq 10^6)


解题思路

注意到(n)很小,可以考虑枚举计算所有路径的最小花费,先构一个(Trie)即可处理这部分。

然后考虑怎么覆盖的问题,和[NOI2008] 志愿者招募类似的,我们可以通过边调走流来实现覆盖问题。

先原点向每一个叶子连流量为1的边,设(siz_x)表示(x)的子树中有多少个叶子那么(x)(fa_x)连接流量为(siz_x-1)的边,然后根向汇点连流量为(siz_1)的边。

这样就保证了每条边都至少有一个流量被调走,但是需要考虑单链上的重复覆盖所以还需要每个点向儿子连无向流量的边。


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
",sum);
		for(ll i=2;i<=tot;i++)
			if(a[i].typ){
				while(a[i].w)
					printf("%lld %lld %lld
",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
",ans);
	if(t)NF::Print();
	return 0;
}
原文地址:https://www.cnblogs.com/QuantAsk/p/15024142.html