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)