正题

题目链接: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. 如何在WPF中定义窗体模板

    参考网址:https://www.cnblogs.com/chenxizhang/archive/2010/01/10/1643676.html可以在app.xaml中定义一个ControlTempl ...

  2. Ubuntu的build-essential有什么作用

    Ubuntu缺省情况下,并没有提供C/C++的编译环境,因此还需要手动安装.但是如果单独安装gcc以及g++比较麻烦,幸运的是,Ubuntu提供了一个build-essential软件包.查看该软件包 ...

  3. ubuntu下配置JDK的一些坑点

    ubuntu下配置JDK的一些坑点 在centos下的JDK配置: 在ubuntu下的话,要修改两个地方: 在/etc/enviornment中配置! 在/etc/profile中配置! 写在最后: ...

  4. Sentinel限流、降级配置详解

    安装Sentinel 下载sentinel-dashboard-1.8.2.jar 安装有jdk环境,8080端口未被占用 在jar包所在目录打开cmd,输入命令启动:java -jar sentin ...

  5. Redis(二):基本数据类型

    基础 # redis默认有16个数据库,数组下标从0开始,默认使用0号库 # 当我们启动服务器并连接客户端之后: set <key> <value> # 向数据库中添加数据用于 ...

  6. rEFI引导Win10+Ubuntu14双系统

    公司买了一台Alienware 15 R2,安装双系统折腾死我了,现在记录一下安装过程. 硬盘: 256固态+1T机械 安装顺序: 先Windows,再Ubuntu 不同BIOS启动方式下安装系统 U ...

  7. 程序挂了之后别再跟我说让我帮你重启啦! 让supervisor帮你搞定...

    目录 有啥用? 安装 生成配置文件 启动supervisor 自定义配置文件 控制命令 求关注啦 有啥用? 很多我们项目排期进入联调.测试阶段,如果QA同学是直接跟你要一个后端环境的话,那简单点大概率 ...

  8. MMM双主-双从读写分离部署

    原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 前期规划 1.1 主机规划 1.2 虚拟IP规划 1.3 用户列表 1.4 整 ...

  9. Java的Class类及static块的执行时机

    要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工程由Class对象完成,它包含了与类有关的信息.Java使用Class对象来执行其RTTI,即使你执行的是类似转型 ...

  10. bat 使用ftp进行文件上传

    实例 @echo off rem 打开远程ftp echo open 172.16.137.23 > ftpconfig.txt rem 使用匿名用户登录 echo user anonymuou ...