【LOJ#3026】管道监控
题目
题目链接:https://loj.ac/p/3026
\(n\leq 500,m\leq 10^5,\sum |s_i|\leq 10^6\)。
思路
我们可以把字符串倒着插入一棵 Trie,然后枚举给出的树上的所有点 \(x\),同时在这个节点和 Trie 树上跳。如果当前跳到点 \(y\),此时 Trie 树上跳到的点恰好是某一个字符串的结尾,那么我们就从 \(y\) 向 \(x\) 连一条流量为 \(1\),费用为匹配的字符串的代价的边。
然后我们设点 \(i\) 子树内有 \(f_i\) 个叶子,那么就从 \(i\) 的父亲向 \(i\) 连一条流量为 \(f_i\),费用为 \(0\) 的边,同时从 \(i\) 向 \(i\) 的父亲连一条流量为 \(+\infty\),费用为 \(0\) 的边。
最后从源点向 \(1\) 连流量为 \(+\infty\),费用为 \(0\) 的边,每一个叶子向汇点连流量为 \(1\),费用为 \(0\) 的边,跑最小费用最大流即可。
这个思路和昨天比赛志愿者招募是几乎一样的。不再赘述。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=510,M=1000010;
const ll Inf=1e18;
int n,m,t,S,T,tot=1,head[N],fa[N],flw[N],pre[N];
ll cost,maxf,dis[N];
char s[M],c[N];
bool vis[N];
struct edge
{
int next,to,flow,cost,id;
}e[M];
void add(int from,int to,int flow,int cost,int id=0)
{
e[++tot]=(edge){head[from],to,flow,cost,id};
head[from]=tot;
swap(from,to);
e[++tot]=(edge){head[from],to,0,-cost,id};
head[from]=tot;
}
struct Trie
{
int tot,ch[M][26],cost[M],id[M];
Trie() { memset(cost,0x3f3f3f3f,sizeof(cost)); tot=1; }
void ins(char *s,int val,int k)
{
int p=1,len=strlen(s+1);
for (int i=len;i>=1;i--)
{
if (!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++tot;
p=ch[p][s[i]-'a'];
}
if (val<cost[p]) cost[p]=val,id[p]=k;
}
void addedge(int x)
{
int p=1;
for (int y=x;fa[y];y=fa[y])
{
if (!ch[p][c[y]-'a']) break;
p=ch[p][c[y]-'a'];
if (cost[p]<Inf) add(fa[y],x,1,cost[p],id[p]);
}
}
}trie;
bool spfa()
{
memset(dis,0x3f3f3f3f,sizeof(dis));
deque<int> q;
q.push_back(S); dis[S]=0;
while (q.size())
{
int u=q.front(); q.pop_front();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (e[i].flow && dis[v]>dis[u]+e[i].cost)
{
dis[v]=dis[u]+e[i].cost; pre[v]=i;
if (!vis[v])
{
vis[v]=1;
if (q.size() && dis[v]<=dis[q.front()]) q.push_front(v);
else q.push_back(v);
}
}
}
}
return dis[T]<Inf;
}
void addflow()
{
int minf=Inf;
for (int i=T;i!=S;i=e[pre[i]^1].to)
minf=min(minf,e[pre[i]].flow);
for (int i=T;i!=S;i=e[pre[i]^1].to)
e[pre[i]].flow-=minf,e[pre[i]^1].flow+=minf;
cost+=dis[T]*minf; maxf-=minf;
}
void MCMF()
{
while (spfa()) addflow();
}
signed main()
{
memset(head,-1,sizeof(head));
S=N-1; T=N-2;
scanf("%lld%lld%lld",&n,&m,&t);
for (int i=2;i<=n;i++)
{
scanf("%lld",&fa[i]);
while (c[i]=getchar())
if (c[i]>='a' && c[i]<='z') break;
flw[i]=1; flw[fa[i]]=0;
}
add(S,1,Inf,0);
for (int i=1;i<=n;i++)
if (flw[i]) add(i,T,1,0),maxf++;
for (int i=n;i>=1;i--)
{
add(fa[i],i,flw[i]-1,0);
add(i,fa[i],Inf,0);
flw[fa[i]]+=flw[i];
}
for (int i=1,x;i<=m;i++)
{
scanf("%lld%s",&x,s+1);
trie.ins(s,x,i);
}
for (int i=1;i<=n;i++)
trie.addedge(i);
MCMF();
if (maxf) return printf("-1"),0;
cout<<cost<<"\n";
if (t)
{
cost=0;
for (int i=2;i<=tot;i+=2)
if (!e[i].flow && e[i].id) cost++;
cout<<cost<<"\n";
for (int i=2;i<=tot;i+=2)
if (!e[i].flow && e[i].id)
cout<<e[i^1].to<<" "<<e[i].to<<" "<<e[i].id<<"\n";
}
return 0;
}