NOIP2018T1DAY1——Road(并查集做法??)

题目描述

春春是一名道路工程师,负责铺设一条长度为 nnn 的道路。 铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 nnn 块首尾相连的区 域,一开始,第 iii 块区域下陷的深度为 did_idi​ 。 春春每天可以选择一段连续区间[L,R] [L,R][L,R] ,填充这段区间中的每块区域,让其下陷深 度减少 111。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 000 。 春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变 为 000 。

输入输出格式

输入格式:

输入文件包含两行,第一行包含一个整数 nnn,表示道路的长度。 第二行包含 nnn 个整数,相邻两数间用一个空格隔开,第i ii 个整数为 did_idi​ 。

输出格式:

输出文件仅包含一个整数,即最少需要多少天才能完成任务。

输入输出样例

输入样例#1:

6   
4 3 2 5 3 5 

输出样例#1:

9

说明

一种可行的最佳方案是,依次选择:[1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]
对于 30% 的数据,1≤n≤10
对于 70% 的数据,1≤n≤1000
对于 100% 的数据,1≤n≤100000,0≤di≤10000

我估计全NOIP没有人和我一样写并查集的了

可以发现我们可以先把所有最低的先填起来再填高的

这样是没有影响的

每一次把一块填起来就相当于把低的向高的合并,花费是他们的高度差

然后就可以用并查集维护,找左边和右边中的较低合并

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();
    int res=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return res;
} 
int n,pos[100005],fa[100005];
struct node{
    int l,r,h;
}a[100005];
inline bool comp(int x,int y){
    return a[x].h>a[y].h;
}
inline int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int ans;
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i].h=read(),a[i].l=i,a[i].r=i,fa[i]=i,pos[i]=i;
    sort(pos+1,pos+n+1,comp);
    for(int i=1;i<=n;i++){
        int f1=find(a[pos[i]].l-1),f2=find(a[pos[i]].r+1),k=find(pos[i]);
        if(a[f1].h<a[f2].h){
            ans+=a[k].h-a[f2].h,fa[k]=f2,a[f2].l=a[k].l;
        }
        else {
            ans+=a[k].h-a[f1].h,fa[k]=f1,a[f1].r=a[k].r;
        }
    }
    cout<<ans;
}

最后
推广一下另外几篇题解:
DAY1T1:[铺设道路]:(并查集??)
DAY1T2:[货币系统]:(完全背包/搜索)
DAY1T3:[赛道修建]:(二分答案+贪心策略)
DAY2T1:[旅行]:(基环树搜索)
DAY2T2:[填数游戏]:(暴力搜索找规律)
DAY2T3:[保卫王国]:(动态dp+Splay)

原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366398.html