1. 程式人生 > 實用技巧 >#二叉堆#JZOJ 4320 旅行 From 2020年8月11日提高B組

#二叉堆#JZOJ 4320 旅行 From 2020年8月11日提高B組


分析

有一個很重要的性質就是如果把兩個點到根節點的路徑長加起來就是兩個點間的路徑長(正負消掉了)
而且眾所周知的是奇數+偶數=奇數
可以預處理每個點到根節點的路徑長度(按照題目要求)
然後把\(n\)個點分成深度為奇數和偶數兩個部分,
那就可以套洛谷 1631 序列合併


程式碼

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=100011; typedef long long lll;
struct node{int y,w,next;}e[N<<1];
struct rec{
    int rk1,rk2;
	lll w;
	bool operator <(const rec &t)const{
		return w<t.w;
	}
}heap[N];
lll odd[N],even[N],dis[N],ans;
int m,cnt,dep[N],n1,n2,as[N],n,k=1;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans; 
}
inline void Swap(rec &t1,rec &t2){rr rec t=t1; t1=t2; t2=t;}
inline void Push(rec now){
	heap[++cnt]=now;
	rr int x=cnt;
	while (x>1){
		if (heap[x]<heap[x>>1])
		    Swap(heap[x>>1],heap[x]),x>>=1;
		else return;
	}
}
inline void Pop(){
	heap[1]=heap[cnt--];
	rr int x=1;
	while ((x<<1)<=cnt){
		rr int y=x<<1;
		if (y<cnt&&heap[y+1]<heap[y]) ++y;
		if (heap[y]<heap[x]) Swap(heap[y],heap[x]),x=y;
	    else return;
	}
}
inline void dfs(int x,int fa){
	for (rr int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa){
		dis[e[i].y]=e[i].w-dis[x],
		dep[e[i].y]=dep[x]+1,dfs(e[i].y,x);
	}
}
signed main(){
	n=iut(),m=iut();
	for (rr int i=1;i<n;++i){
		rr int x=iut(),y=iut(),w=iut();
		e[++k]=(node){y,w,as[x]},as[x]=k;
		e[++k]=(node){x,w,as[y]},as[y]=k;
	}
	dfs(1,0);
	for (rr int i=1;i<=n;++i)
	if (dep[i]&1) odd[++n1]=dis[i];
	    else even[++n2]=dis[i];
	if (n1<m/n2) return !printf("Stupid Mike");
	sort(odd+1,odd+1+n1),sort(even+1,even+1+n2);
	for (rr int i=1;i<=n1;++i) Push((rec){i,1,odd[i]+even[1]});
	for (rr int i=1;i<m;++i){
		rr rec Ans=heap[1]; Pop();
		if (Ans.rk2<n2) Push((rec){Ans.rk1,Ans.rk2+1,odd[Ans.rk1]+even[Ans.rk2+1]});
	}
	return !printf("%lld",heap[1].w);
}