首页 > 其他分享 >P7563 JOISC 2021 Day4 最悪の記者 4 (Worst Reporter 4)

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

时间:2024-01-24 21:45:05浏览次数:23  
标签:lazy Reporter int tree Day4 min mi 記者 maxn

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);
}

标签:lazy,Reporter,int,tree,Day4,min,mi,記者,maxn
From: https://www.cnblogs.com/binbinbjl/p/17985925

相关文章

  • 算法学习Day41整数拆分、不同的二叉搜索树
    Day41整数拆分、不同的二叉搜索树ByHQWQF2024/01/22笔记343.整数拆分给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。返回你可以获得的最大乘积。示例1:输入:2输出:1解释:2=1+1,1×1=1。示例 2:输入:10输出:36解释......
  • day47
    day4702.今日内容概要如何查询表"""selectwheregroupbyhavingdistinctorderbylimitregexplike..."""连表操作理论03.前期表准备及注意事项#创建表createtableemp(idintnotnulluniqueauto_inc......
  • day49
    conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='wang123',database='db5',charset='utf8'#编码千万不要加-)cusor=conn.cursor()#括号内不加参数的话查询出来的是元组的形式数据不够明确容易混乱cursor=conn.cur......
  • day40 如何运维管理超1k+node节点 - 站在面试官角度谈面试 (13-14)
    13、如何运维管理超1k+node节点(四节)一、数据背景100,000+Pods1300+Nodes3集群(单:11Master+17ETCD)ToC服务行业二、瓶颈问题Apiserver调度,延迟问题;Controller不能及时从APIServer感知到最新的变化,处理的延时较高;Scheduler延迟高、吞吐低,无法适应业务日常需求;ET......
  • day4
    今天差不多回顾完了整本张宇基础30讲无穷级数这一块知识结构可算清晰了,别再混了在空间解析几何这一块,着重处理了直线到平面投影的概念,知道了标准化流程分别是求垂面和联立,其他没什么难的看了最后一章,没什么难的,今后做到题再说。。。。。。。。下午做微分方程那一张的习题时......
  • Day49 创建对象内存分析
    创建对象内存分析Pet.javapackagecom.oop.demo03;publicclassPet{publicStringname;publicintage;//无参构造publicvoidshout(){System.out.println("叫了一声");}}Application.javapackagecom.oop;importcom.oop.demo......
  • 2024-1-13 DAY4
    2024-1-13DAY4B-IntegralArray#include<bits/stdc++.h>#defineendl'\n'#defineintlonglongusingnamespacestd;constintN=1e6+10;intn,c;voidsolve(){ cin>>n>>c; vector<int>cnt(c*2,0); for(i......
  • Day48 构造器详解
    构造器详解类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:1.必须和类的名字相同2.必须没有返回类型,也不能写void构造器必须要掌握1.Person.class文件与Person.java文件进行对比在idea的out文件夹下面打开同名的class文......
  • Day4
    1.给定一个列表 list_1,里面嵌套了多个列表,请你计算出每个嵌套列表的最大值,并输出所有最大值的平均值。测试数据:[[54,28,88,99,77],[99,6,37,68,83],[90,52,36,4,53],[85,66,11,11,61],[20,52,9,81,61],[23,67,37,39,18],[21,36,66,80,30],[74,80,5,......
  • Day41 二维数组
    二维数组多维数组多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。二维数组ina[][]=newint[2][5];以上二维数组a可以看成一个两行五列的数组。二维数组模型图示代码演练packagecom.baixiaofan.array;publiccla......