Loj#3026-「ROIR 2018 Day1」管道监控【Trie,费用流】
正题
题目大意
给出\(n\)个点的一棵外向树,然后\(m\)个字符串和费用表示你每次可以花费这个费用覆盖路径字符串和给出字符串相等的路径,求覆盖所有边的最小花费(可以重复覆盖)
输出方案
\(1\leq n\leq 500,1\leq m\leq10^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\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;
}
Loj#3026-「ROIR 2018 Day1」管道监控【Trie,费用流】的更多相关文章
- 2018.10.15 loj#6010. 「网络流 24 题」数字梯形(费用流)
传送门 费用流经典题. 按照题目要求建边. 为了方便我将所有格子拆点,三种情况下容量分别为111,infinfinf,infinfinf,费用都为validi,jval_{id_{i,j}}valid ...
- 2018.10.15 loj#6013. 「网络流 24 题」负载平衡(费用流)
传送门 费用流sb题. 直接从sss向每个点连边,容量为现有物品量. 然后从ttt向每个点连边,容量为最后库存量. 由于两个点之间可以互相任意运送物品,因此相邻的直接连infinfinf的边就行了. ...
- LOJ#2351. 「JOI 2018 Final」毒蛇越狱
LOJ#2351. 「JOI 2018 Final」毒蛇越狱 https://loj.ac/problem/2351 分析: 首先有\(2^{|?|}\)的暴力非常好做. 观察到\(min(|1|,| ...
- 「BalkanOI 2018 Day1」Election
「BalkanOI 2018 Day1」Election 记C为1,T为-1,\(sum[i]\)为\(i\)点的前缀和. 对于询问\([l,r]\),分两步计算答案. 要求所有点的\(sum[i]- ...
- 「BalkanOI 2018 Day1」Minmaxtree
「BalkanOI 2018 Day1」Minmaxtree 每个点都有一个最大和最小权值的限制. 然后每一个权值的限制都必须要取到. 每个点显然可以直接让他取到最大或最小权值. 可以想到每个点匹配一 ...
- LOJ#2632. 「BalticOI 2011 Day1」打开灯泡 Switch the Lamp On
题目描述 译自 BalticOI 2011 Day1 T3「Switch the Lamp On」有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会.有 N×M 个这样 ...
- 2018.10.14 loj#6003. 「网络流 24 题」魔术球(最大流)
传送门 网络流好题. 这道题可以动态建图. 不难想到把每个球iii都拆点成i1i_1i1和i2i_2i2,每次连边(s,i1),(i2,t)(s,i_1),(i_2,t)(s,i1),(i2, ...
- LOJ2321. 「清华集训 2017」无限之环【费用流】
LINK 很好的一道网络里题 首先想插头DP的还是出门左转10分代码吧 然后考虑怎么网络流 首先要保证没有漏水 也就是说每个接口一定要有对应的接口 那么发现每个点只有可能和上下左右四个点产生联通关系 ...
- 【LOJ】#3032. 「JOISC 2019 Day1」馕
LOJ#3032. 「JOISC 2019 Day1」馕 处理出每个人把馕切成N段,每一段快乐度相同,我们选择第一个排在最前的人分给他的第一段,然后再在未选取的的人中选一个第二个排在最前的切一下,并把 ...
随机推荐
- 接口和包--Java学习笔记
接口 定义及基础用法 interface定义:没有字段的抽象类 interface person{ void hello(); String getName(); } /*接口本质上就是抽象类 abs ...
- js 遍历数组对象求和
这个通常是求多个商品的总价遇到的情形: [ 0: {id: 1, name: "服务费", price: "1.00"} 1: {id: 2, name: &q ...
- 0x800b010a 证书
无论是装微软的什么应用,只要报这个错误,下载这个证书: http://download.microsoft.com/download/2/4/8/248D8A62-FCCD-475C-85E7-6ED ...
- Vue实现在前端导出Excel 方法1
也可以去看我的方法2:https://www.cnblogs.com/yingyigongzi/p/10915403.html ------------------------------------ ...
- JAVA集合类(代码手写实现,全面梳理)
参考网址:https://blog.csdn.net/weixin_41231928/article/details/103413167 目录 一.集合类关系图 二.Iterator 三.ListIt ...
- 玩转Spring生命周期之Lifecycle
Lifecycle callbacks Initialization callbacks.Destruction callbacks要与容器的bean生命周期管理交互,即容器在启动后和容器在销毁前对每 ...
- 线程池ExecutorService的使用
转载自: 海子 Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短 ...
- 搞懂Redis协议RESP
RESP (REdis Serialization Protocal) Redis客户端和服务端之间通信的协议.它很简单,建立在TCP协议上,提供简单.高性能.可读性强的数据序列化的规范和语义. 5种 ...
- 解决移动端click事件300ms延迟的问题
方法1.部分浏览器的<meta>标签加上width=device-width就能解决. 方法2.引入fastclick.js库 <!DOCTYPE html> <html ...
- C# ArrayPool 源码解读之 byte[] 池化
一:背景 1. 讲故事最近在分析一个 dump 的过程中发现其在 gen2 和 LOH 上有不少size较大的free,仔细看了下,这些free生前大多都是模板引擎生成的html片段的byte[]数组 ...