1. 程式人生 > 實用技巧 >【2020.11.30提高組模擬】刪邊(delete) 題解

【2020.11.30提高組模擬】刪邊(delete) 題解

【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

差點抱玲了(玲醬這麼可愛為什麼不抱抱呢?大霧),還好最後想到了逆向思維。

評測機出鍋了,

彷彿回到了去年暑假的那些日子呢