P7563 JOISC 2021 Day4 最悪の記者 4 (Worst Reporter 4)

线段树合并好题,通过线段树合并特别的方式优化了树形 dp。

思路

根据图中的不等关系连边建图,不难发现最后的图将会是基环树森林和普通的树的森林,我们先考虑对于一棵树要怎么办。

将 \(h_i\) 离散化,\(m\) 为离散化上界,使用树形 dp。

设 \(f_{i,j}\) 为将 \(i\) 改成 \(j\) 使 \(i\) 的子树内满足不等关系的最小花费。

\[f_{i,j}=c_i\times[j\neq h_i]+\sum_{k\in i.sons} \min_{j\leq t \leq m} f_{k,t}
\]

这个 dp 是超时的,但是是正确的,我们考虑优化 dp 转移。

我们发现每个都加上 \(c_i\) 对我们操作有点麻烦,我们在最后求出答案是同一加上 \(\sum c_i\),将方程改为

\[f_{i,j}=\sum_{k\in i.sons}\min_{j\leq t\leq m} f_{k,t} -c_i*[j=h_i]
\]

为什么这样操作呢?

由于转移只有区间最小值查询和减法操作,考虑线段树维护 \(f\) 值。

线段树的区间维护一个节点的状态的第二维,点取值维护对应区间的最小值,区间 \([l,r]\) 维护的是 \(\min_{l\leq i\leq r} f_{u,i}\)。

每个点开一棵肯定不现实,考虑线段树合并,每一次合并就相当于父亲和儿子做一次转移。

找区间最小值的区间是 \([j,m]\),合并树 \(u\) 和树 \(v\) 时,计 \(u_{min}\) 和 \(v_{min}\) 为各自的最小值(在区间 \([j,m]\) 内),对于节点 \(p\) 和 \(q\) 分类讨论 \(p+v_{min}\) 和 \(q+u_{min}\) 哪个最小(这里实际上和转移有关,可以钦定父子关系,从转移方程的角度分析),将较小值设置即可。

由于最小值右端点固定,启发性的先合并右子树,便于维护最小值。

对于每个点的初始更新,在 \([h_i,h_i]\) 出加上 \(-c_i\) 即可。

扩展到基环树,发现基环树的环上的点肯定是同一取值,且要么是 \(1\) 要么是环上取值。

那么先求出基环树的环上节点的 \(f\) 状态,最后枚举环上的点的取值即可。

CODE

#include<bits/stdc++.h>
using namespace std; #define ll long long
#define lch(p) tree[p].lch
#define rch(p) tree[p].rch const int maxn=2e5+5; int rb; struct linetree
{
int tot;
ll d1,d2;
struct treenode{int lch,rch;ll lazy,mi;}tree[maxn*57];
void push_down(int p)//下传懒标记
{
if(!tree[p].lazy) return ;
ll &lazy=tree[p].lazy;
if(lch(p)) tree[lch(p)].lazy+=lazy,tree[lch(p)].mi+=lazy;
if(rch(p)) tree[rch(p)].lazy+=lazy,tree[rch(p)].mi+=lazy;
lazy=0;
}
void updata(int p)
{
tree[p].mi=min(tree[lch(p)].mi,tree[rch(p)].mi);
}
void insert(int &p,int l,int r,int x,ll y)
{
if(l>x||r<x) return ;
if(!p) p=++tot;
if(l==r){tree[p].mi=y;return ;}
push_down(p);
int mid=(l+r)>>1;
insert(lch(p),l,mid,x,y);
insert(rch(p),mid+1,r,x,y);
updata(p);
}
ll qry(int p,int l,int r,int lx,int rx)//查询区间最小值
{
if(!p) return 0;
if(lx<=l&&r<=rx) return tree[p].mi;
if(lx>r||rx<l) return 0;
push_down(p);
int mid=(l+r)>>1;
return min(qry(lch(p),l,mid,lx,rx),qry(rch(p),mid+1,r,lx,rx));
}
void merge(int &p1,int p2,int l,int r)//合并
{
if(!p1&&!p2) return ;
if(!p1)
{
d2=min(d2,tree[p2].mi);
tree[p2].mi+=d1;
tree[p2].lazy+=d1;
p1=p2;
return ;
}
else if(!p2)
{
d1=min(d1,tree[p1].mi);
tree[p1].mi+=d2;
tree[p1].lazy+=d2;
return ;
}
if(l==r)
{
d1=min(d1,tree[p1].mi),d2=min(d2,tree[p2].mi);
if(tree[p1].mi+d2<=tree[p2].mi+d1){tree[p1].mi=tree[p1].mi+d2;}
else{tree[p1].mi=tree[p2].mi+d1;}
return ;
}
push_down(p1);
push_down(p2);
int mid=(l+r)>>1;
merge(rch(p1),rch(p2),mid+1,r);//启发式
merge(lch(p1),lch(p2),l,mid);
updata(p1);
}
void premrg(int &x,int y)
{
d1=d2=0;
merge(x,y,1,rb);
}
}T;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*2];
void add(int u,int v)
{
tot++;
edge[tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
}E; int n,tot;
int a[maxn],h[maxn],c[maxn],ind[maxn],d[maxn],dfn[maxn];
int rt[maxn],nt[maxn]; struct node{ll h,c;};
bool cmp(node a,node b){return a.h<b.h;}
vector<node>cr[maxn]; ll ans; void pushcir(int u)//同一个环赋予同一编号
{
dfn[u]=tot;
cr[tot].push_back({h[u],c[u]});
if(!dfn[a[u]]) pushcir(a[u]);
}
void gtp()//找环
{
queue<int>que;
while(!que.empty()) que.pop();
for(int i=1;i<=n;i++) if(!ind[i]) que.push(i);
while(!que.empty())
{
int u=que.front();que.pop();
if(!--ind[a[u]]) que.push(a[u]);
}
for(int i=1;i<=n;i++)//可能有多个环,tot 是环数
if(!dfn[i]&&ind[i]) tot++,pushcir(i);
}
void dfs(int u)//求 u 的状态
{
for(int i=E.head[u];i;i=E.edge[i].nxt)
{
int v=E.edge[i].to;
dfs(v);
T.premrg(rt[u],rt[v]);//合并儿子和自己
}
T.insert(rt[u],1,rb,h[u],T.qry(rt[u],1,rb,h[u],rb)-c[u]);
//insert 更改区间 [h[u],h[u]] 的值
}
void calc()
{
for(int i=1;i<=n;i++) if(!ind[i]&&ind[a[i]])//求环的状态
dfs(i),T.premrg(nt[dfn[a[i]]],rt[i]);
for(int i=1;i<=tot;i++)
{
sort(cr[i].begin(),cr[i].end(),cmp);
ll cnt=T.tree[nt[i]].mi,sh=cr[i][0].h,sc=0;
for(node v:cr[i])
{
if(v.h==sh) sc+=v.c;
else
{
cnt=min(cnt,T.qry(nt[i],1,rb,sh,rb)-sc);
sh=v.h,sc=v.c;
}
}
cnt=min(cnt,T.qry(nt[i],1,rb,sh,rb)-sc);
ans+=cnt;
}
} int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a[i],&h[i],&c[i]);
E.add(a[i],i);
ind[a[i]]++;
d[i]=h[i];
ans+=c[i];
}
sort(d+1,d+n+1);
rb=unique(d+1,d+n+1)-d-1;
for(int i=1;i<=n;i++) h[i]=lower_bound(d+1,d+rb+1,h[i])-d;//离散化
gtp();
calc();
printf("%lld",ans);
}

P7563 JOISC 2021 Day4 最悪の記者 4 (Worst Reporter 4)的更多相关文章

  1. LOJ#2882. 「JOISC 2014 Day4」两个人的星座(计算几何)

    题面 传送门 题解 我们发现如果两个三角形相离,那么这两个三角形一定存在两条公切线 那么我们可以\(O(n^2)\)枚举其中一条公切线,然后可以暴力\(O(n^3)\)计算 怎么优化呢?我们可以枚举一 ...

  2. @loj - 3039@ 「JOISC 2019 Day4」蛋糕拼接 3

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 今天是 IOI 酱的生日,所以她的哥哥 JOI 君给她预定了一个 ...

  3. 「JOISC 2020 Day4」首都城市

    题目   点这里看题目. 分析   做法比较容易看出来.我们对于每个城市,找出那些 " 如果这个城市在首都内,则必须在首都内的其它城市 " ,也就是为了让这个城市的小镇连通而必须选 ...

  4. 「JOISC 2014 Day4」两个人的星座

    首先突破口肯定在三角形不交,考虑寻找一些性质. 引理一:两个三角形不交当且仅当存在一个三角形的一条边所在直线将两个三角形分为异侧 证明可以参考:三角形相离充要条件,大致思路是取两个三角形重心连线,将其 ...

  5. Solution -「JOISC 2021」古老的机器

    \(\mathcal{Description}\)   Link.   这是一道通信题.   对于长度为一个 \(n\),仅包含字符 X, Y, Z 的字符串 \(s\),将其中 \(n\) 个字符按 ...

  6. Solution -「JOISC 2021」「LOJ #3489」饮食区

    \(\mathcal{Description}\)   Link.   呐--不想概括题意,自己去读叭~ \(\mathcal{Solution}\)   如果仅有 1. 3. 操作,能不能做?    ...

  7. Solution -「JOISC 2021」「LOJ #3495」聚会 2

    \(\mathcal{Description}\)   Link.   给定一棵含 \(n\) 个结点的树.称点集 \(S\) 到结点 \(u\) 的会合距离为 \(\sum_{v\in S}\ope ...

  8. Solution -「JOISC 2021」「LOJ #3491」道路建设

    \(\mathcal{Description}\)   Link.   平面上有 \(n\) 个互不重合的点 \((x_{1..n},y_{1..n})\),求其两两曼哈顿距离的前 \(m\) 小值. ...

  9. 「JOISC 2019 Day4」蛋糕拼接 3

    loj 3039 NKOJ Description \(n\)个蛋糕,每个蛋糕有\(w_i,h_i\).选\(m\)个蛋糕满足\(\sum\limits_{j=1}^mw_{k_j}-\sum\lim ...

  10. Solution -「简单 DP」zxy 讲课记实

    魔法题位面级乱杀. 「JOISC 2020 Day4」治疗计划 因为是不太聪明的 Joker,我就从头开始理思路了.中途也会说一些和 DP 算法本身有关的杂谈,给自己的冗长题解找借口. 首先,治疗方案 ...

随机推荐

  1. apr库编译及队列使用笔记

    操作系统 :CentOS 7.9_x64 apr库版本:apr-1.7.4 & apr-util-1.6.3 gcc 版本:4.8.5 队列功能在C++或Python等脚本语言里面,是很容易就 ...

  2. WinForm UI 库

    WinForm UI库 HZH_Controls HZHControls是基于.Net Framework4.0原生控件开发完全开源的一套控件,你不需要担心有其他控件或版权问题.提供完整的示例代码,方 ...

  3. 【Java】之获取CSV文件数据以及获取Excel文件数据

    一.获取CSV文件数据 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Sheet; impor ...

  4. redis 基准性能测试与变慢优化

    redis 参考目录: 生产级Redis 高并发分布式锁实战1:高并发分布式锁如何实现 https://www.cnblogs.com/yizhiamumu/p/16556153.html 生产级Re ...

  5. 音视频FAQ(二)视频直播延时高

    摘要 延时高是实时互动技术中常见的问题之一,解决延时高问题需要综合考虑网络.设备.编解码算法等多个因素.解决方案包括优化设备端延时.优化网络传输延时和使用UDP进行音视频传输等.在选择音视频传输协议时 ...

  6. JavaScript – Generator Function

    参考 阮一峰 – Generator 函数的语法 介绍 Generator Function 是一种特别的函数, 它让函数有一种分阶段执行的能力. 一般的函数, 你调用它, 它执行所有函数内的代码, ...

  7. Vs Code, Visual Studio 2022, Angular and Live Server Running Through Https and IP Address

    前言 之前就写过 angular cli, vs code liveserver, vs 2019 iis express 10, vs code kestrel 使用 https + ip. 但写的 ...

  8. Naming Conversion & Case Style 命名规范

    前言 写代码有 2 个点很重要 第一是表达 (不要词不达意) 要达到这点, 就要多参考其它人如何表达. 第二是一致性 (一样的东西就用一样的写法) 要达到这点就要建立规范 以前的笔记 命名规范 nam ...

  9. Spring —— 注解开发(总结)

    XML配置与注解配置对比   

  10. 加快 hdfs block 块复制的参数调整

    共涉及三个参数: dfs.namenode.replication.max-streams 30 => 70 dfs.namenode.replication.max-streams-hard- ...