正题

题目链接: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. C# 获取计算机的硬件、操作系统信息

    C# 获取计算机的硬件.操作系统信息   获得信息的命名空间是:System.Management 创建ManagementObjectSearcher对象获取相关信息 eg: using Syste ...

  2. Windows下NodeJS安装与npm环境变量配置

    node.js下载:https://nodejs.org/en/download/ 参考:https://www.jianshu.com/p/812de13f1276 1.安装过程基本直接" ...

  3. SpringBoot中的静态资源访问

    一.说在前面的话 我们之间介绍过SpringBoot自动配置的原理,基本上是如下: xxxxAutoConfiguration:帮我们给容器中自动配置组件: xxxxProperties:配置类来封装 ...

  4. springboot与通用mapper的整合

    找到springboot工程下的pom.xml文件,导入如下的依赖jar包 <!--配置通用Mapper start--> <dependency> <groupId&g ...

  5. SpringBoot2.0 防止XSS攻击

    一:什么是XSS XSS攻击全称跨站脚本攻击,是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中. 你可以自己做个简单尝试: 1. 在任何一个表单内,你输 ...

  6. MediaWiki定制化改动

    Linux下面安装MediaWiki环境的方法,可以参照我上一篇文章linux使用xampp安装MediaWiki环境 重置用户密码 使用维护脚本 可以使用maintenance/changePass ...

  7. MySQL主主互备不同步的解决方法

    MySQL主主互备不同步 首先在服务器上执行show slave satus;可以看到很多同步的参数: Master_Log_File: SLAVE中的I/O线程当前正在读取的主服务器二进制日志文件的 ...

  8. Java基础(二)——内部类

    一.内部类 内部类(Inner Class)就是定义在一个类里面的类.与之对应,包含内部类的类被称为外部类.内部类可以用private修饰. 1.为什么要定义内部类?或者内部类的作用是什么? 内部类提 ...

  9. Redis中关于key的操作指令

    1.keys: 例如: 2.exists 3.move 将指定的数据移动到指定的库 4.expire 5.tt1 6.type 7.rename 8.del

  10. python3 爬虫五大模块之五:信息采集器

    Python的爬虫框架主要可以分为以下五个部分: 爬虫调度器:用于各个模块之间的通信,可以理解为爬虫的入口与核心(main函数),爬虫的执行策略在此模块进行定义: URL管理器:负责URL的管理,包括 ...