【LOJ#3026】管道監控
阿新 • • 發佈:2021-07-17
題目
題目連結: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\)
最後從源點向 \(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; }