【2020.11.30提高組模擬】刪邊(delete) 題解
阿新 • • 發佈:2020-11-30
【2020.11.30提高組模擬】刪邊(delete) 題解
題意簡述
給一棵樹刪邊,每次刪的代價為這條邊所連的兩個點的子樹中最大點權值。
求刪光的最小代價。
\(n\le100000\).
Solution
正著思考發現沒有什麼好的思路,貪心的話會後效性。不妨反過來考慮。
這時題目變成了:給\(n\)個點,每次連通兩個點集,代價為兩個點集中最大點權之和。
例如這個圖
首先,每個點都是獨立的。
那麼你會先加入\(1-2\)還是\(2-3\)呢?
如果先加入\(2-3\),代價為\(2+3\),接下來再加入點\(1\)時,\(2-3\)所產生的貢獻是\(3\)。
如果而後還有一些集合需要並進來時,當前集合所產生的貢獻為\(3\)
所以,我們要讓點權大的點以後再合併,點權小的點先合併。
所以初始化時先把邊權設為其所連兩點的點權中更大的那一個。對所有邊按照邊權排序,再用類似並查集的方法合併同時維護集合中的最大點權。
以上。
Code
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #include<map> #include<set> #include<queue> #include<vector> #define IL inline #define re register #define LL long long #define ULL unsigned long long #ifdef TH #define debug printf("Now is %d\n",__LINE__); #else #define debug #endif using namespace std; template<class T>inline void read(T&x) { char ch=getchar(); int fu; while(!isdigit(ch)&&ch!='-') ch=getchar(); if(ch=='-') fu=-1,ch=getchar(); x=ch-'0';ch=getchar(); while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} x*=fu; } inline int read() { int x=0,fu=1; char ch=getchar(); while(!isdigit(ch)&&ch!='-') ch=getchar(); if(ch=='-') fu=-1,ch=getchar(); x=ch-'0';ch=getchar(); while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*fu; } int G[55]; template<class T>inline void write(T x) { int g=0; if(x<0) x=-x,putchar('-'); do{G[++g]=x%10;x/=10;}while(x); for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n'); } int n,f[100010],mx[100010],a[100010]; LL ans; int getf(int x) { if(f[x]==x) return x; return f[x]=getf(f[x]); } void merge(int x,int y) { x=getf(x); y=getf(y); if(x!=y) { ans+=mx[x]+mx[y]; mx[x]=max(mx[x],mx[y]); f[y]=x; } } struct edge { int x,y; edge(int xx=0,int yy=0){x=xx,y=yy;} // int v()const{return max(a[x],a[y]);} bool operator<(const edge & z)const { return max(a[x],a[y])<max(a[z.x],a[z.y]); } }; vector<edge>e; int main() { // freopen("delete.in","r",stdin); // freopen("delete.out","w",stdout); n=read(); for(int i=1;i<=n;i++) f[i]=i,mx[i]=a[i]=read(); for(int i=1;i<n;i++) e.push_back(edge(read(),read())); sort(e.begin(),e.end()); for(unsigned i=0;i<e.size();i++) { merge(e[i].x,e[i].y); } cout<<ans; return 0; }
End
差點抱玲了(玲醬這麼可愛為什麼不抱抱呢?大霧),還好最後想到了逆向思維。
評測機出鍋了,
彷彿回到了去年暑假的那些日子呢