模擬賽Day1(20200207) T3 二分題【對樹的重心的充分利用】

題目描述:

在這裏插入圖片描述
在這裏插入圖片描述

題目分析:

先考慮k=nk=n的情況,這是一個經典問題,結論爲所有點到重心的距離和。
證明:
對任意一點uu,都有dis(pi,pi+1)dis(pi,u)+dis(u,pi+1)dis(p_i,p_{i+1})\le dis(p_i,u)+dis(u,p_{i+1})
那麼就有i=1ndis(pi,pimodn+1)2i=1ndis(pi,u)\sum_{i=1}^ndis(p_i,p_{i\bmod n+1})\le2*\sum_{i=1}^ndis(p_i,u)
當點uu爲重心時,上式可以取到等號,即左邊取到最大值。
因爲重心的子樹大小一定n/2\le n/2,形象地理解就是我們可以在不同的子樹間反覆橫跳,構造這樣的序列也是很容易的,比如按照dfs序從1開始:1,1+n/2,2,2+n/2,3,3+n/2…可以發現,每次要麼是跳進子樹,要麼是跳出子樹。
另一種理解是根據Hall定理,一定可以找到反覆橫跳的完備匹配。

根據上面的分析可以看出,k=nk=n時的答案也可以表示爲2u,v,wwmin(siz[v],nsiz[v])2*\sum_{u,v有邊相連,邊權爲w}w*min(siz[v],n-siz[v])
用這種方法解決原問題可以用f[i][j]f[i][j]表示ii的子樹中選了jj個點的最大距離和,轉移時加上每條邊的貢獻,可以做到O(nk)O(nk)

看回原問題,我們相當於是要找到答案的k個點的重心。

考慮枚舉重心,取離重心最遠的k個點,且保證每個子樹中的點不超過k/2個,直接這樣做的複雜度是O(n2logn)O(n^2logn)

在這裏插入圖片描述
確定往哪邊走時可以用nth_element確定前k大,所以問題的複雜度爲O(nlogn)O(nlogn)

關於kk爲奇數的問題暫時沒有理解,存疑

Code:

#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,siz[maxn];
bool vis[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
void Getroot(int u,int ff,int tsz,int &g){
	siz[u]=1; bool flg=1;
	for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=ff)
		Getroot(v,u,tsz,g),siz[u]+=siz[v],flg&=siz[v]<<1<=tsz;
	if(flg&&(tsz-siz[u])<<1<=tsz) g=u;
}
struct node{
	LL d;int p;
	bool operator < (const node &t)const{return d>t.d;}
}a[maxn];
int cnt[maxn],num;
void dfs(int u,int ff,LL dis,int id){
	a[++num]=(node){dis,id};
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff) dfs(v,u,dis+w[i],id?id:v); 
}
void TDC(int u,int tsz){
	Getroot(u,0,tsz,u),vis[u]=1;
	num=0,dfs(u,0,0,0),nth_element(a+1,a+m,a+1+num);
	int v=0;
	for(int i=1;i<=m;i++)
		if(++cnt[a[i].p]>m>>1) {v=a[i].p;break;}
	for(int i=fir[u];i;i=nxt[i]) cnt[to[i]]=0;
	if(v&&!vis[v]) TDC(v,siz[v]<siz[u]?siz[v]:tsz-siz[u]);
	else{
		sort(a+1,a+1+num); LL ans=0;
		for(int i=1,s=0;s<m;i++)
			if(cnt[a[i].p]<m>>1) cnt[a[i].p]++,s++,ans+=a[i].d;
		printf("%lld\n",ans<<1);
	}
}
int main()
{
	read(n),read(m);
	for(int i=1,x,y,z;i<n;i++) read(x),read(y),read(z),line(x,y,z),line(y,x,z);
	TDC(1,n);
}
發佈了379 篇原創文章 · 獲贊 139 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章