1. 程式人生 > >NOIP2018T1DAY1——Road(並查集做法??)

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)