正题

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


题目大意

给出\(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,费用流】的更多相关文章

  1. 2018.10.15 loj#6010. 「网络流 24 题」数字梯形(费用流)

    传送门 费用流经典题. 按照题目要求建边. 为了方便我将所有格子拆点,三种情况下容量分别为111,infinfinf,infinfinf,费用都为validi,jval_{id_{i,j}}valid ...

  2. 2018.10.15 loj#6013. 「网络流 24 题」负载平衡(费用流)

    传送门 费用流sb题. 直接从sss向每个点连边,容量为现有物品量. 然后从ttt向每个点连边,容量为最后库存量. 由于两个点之间可以互相任意运送物品,因此相邻的直接连infinfinf的边就行了. ...

  3. LOJ#2351. 「JOI 2018 Final」毒蛇越狱

    LOJ#2351. 「JOI 2018 Final」毒蛇越狱 https://loj.ac/problem/2351 分析: 首先有\(2^{|?|}\)的暴力非常好做. 观察到\(min(|1|,| ...

  4. 「BalkanOI 2018 Day1」Election

    「BalkanOI 2018 Day1」Election 记C为1,T为-1,\(sum[i]\)为\(i\)点的前缀和. 对于询问\([l,r]\),分两步计算答案. 要求所有点的\(sum[i]- ...

  5. 「BalkanOI 2018 Day1」Minmaxtree

    「BalkanOI 2018 Day1」Minmaxtree 每个点都有一个最大和最小权值的限制. 然后每一个权值的限制都必须要取到. 每个点显然可以直接让他取到最大或最小权值. 可以想到每个点匹配一 ...

  6. LOJ#2632. 「BalticOI 2011 Day1」打开灯泡 Switch the Lamp On

    题目描述 译自 BalticOI 2011 Day1 T3「Switch the Lamp On」有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会.有 N×M 个这样 ...

  7. 2018.10.14 loj#6003. 「网络流 24 题」魔术球(最大流)

    传送门 网络流好题. 这道题可以动态建图. 不难想到把每个球iii都拆点成i1i_1i1​和i2i_2i2​,每次连边(s,i1),(i2,t)(s,i_1),(i_2,t)(s,i1​),(i2​, ...

  8. LOJ2321. 「清华集训 2017」无限之环【费用流】

    LINK 很好的一道网络里题 首先想插头DP的还是出门左转10分代码吧 然后考虑怎么网络流 首先要保证没有漏水 也就是说每个接口一定要有对应的接口 那么发现每个点只有可能和上下左右四个点产生联通关系 ...

  9. 【LOJ】#3032. 「JOISC 2019 Day1」馕

    LOJ#3032. 「JOISC 2019 Day1」馕 处理出每个人把馕切成N段,每一段快乐度相同,我们选择第一个排在最前的人分给他的第一段,然后再在未选取的的人中选一个第二个排在最前的切一下,并把 ...

随机推荐

  1. ReentrantLock中的Condition(等待和唤醒)

    Condition 类的 awiat 方法和 Object 类的 wait 方法等效 Condition 类的 signal 方法和 Object 类的 notify 方法等效 Condition 类 ...

  2. SpringBoot获取请求的参数

    说明 SpringBoot 为我们封装了许多,简便的获取请求参数的方法! 1.获取无注解获取请求参数 请求地址:http://192.168.0.115:8080/myproject/test/noA ...

  3. C#多线程---Event类实现线程同步

    一.简介 我们使用类(.net Framework中的类,如 AutoResetEvent, Semaphore类等)的方法来实现线程同步的时候,其实内部是调用操作系统的内核对象来实现的线程同步. S ...

  4. linux 下文件的权限

  5. JDBC中级篇(MYSQL)——在JDBC中如何获得表中的,自增长的字段值

    注意:其中的JdbcUtil是我自定义的连接工具类:代码例子链接: package c_increment; import java.sql.Connection; import java.sql.P ...

  6. golang中的三个点 ‘...‘ 的用法

    '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是slice可以被打散进行传递. 下面直接上例子: func test1( ...

  7. 聚类算法与K-means实现

    聚类算法与K-means实现 一.聚类算法的数学描述: 区别于监督学习的算法(回归,分类,预测等),无监督学习就是指训练样本的 label 未知,只能通过对无标记的训练样本的学习来揭示数据的内在规律和 ...

  8. Redis的配置文件

  9. scrapy抓取的页面中文会变成unicode字符串

    不了解编码的,需要先补下:http://www.cnblogs.com/jiangtu/p/6245264.html 在学习&使用scrapy抓取网上信息时,发现scrapy 会将含有中文的f ...

  10. Pytest 系列(24)- allure 环境准备

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html allure 和 pytest 相 ...