旅行

description

给定一颗(n)节点的边带权的无根树,求树上任意经过边数为奇数的两点间距离第(k)小.树上两点间的路径(zeta{(u,v)})的距离定义为:从(u)出发,所经过的第奇数条边权乘(1),第偶数条边边权乘上(-1),然后将这些值相加所得结果.

solution

这题有个非常神奇的解法.首先我们选定(1)号节点为根,然后预处理出树上所有点到根的距离(dis),不难发现,所有深度为奇数的点和所有深度为偶数的点相互组合成树上经过边数为奇数的点对集,并且奇数边数路径(xi{(u,v)})的距离可以表示为(dis[u]+dis[v]),于是我们可以把奇数深度节点的(dis)和偶数深度节点的(dis)单独拿出作为两个集合,接下来的任务即求出两集合间取两数相加所得结果的第(k)小.

这里我们用到贪心思想,首先将数组(a)(b)排好序,然后将所有的(a_{i})(b_{1})相加,将其结果压入小根堆内,每次弹出并将(a_{i})(b_{j+1})压入,第(k)次弹出的值即为答案.

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define R register
#define next MabLcdG
#define mod 1
#define Mod(x) ((x%mod+mod)%mod)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline ll read();
inline void write(ll x);
inline void writesp(ll x);
inline void writeln(ll x);
const ll maxn=1100000;
ll n,k; 
ll head[maxn],next[maxn],to[maxn],tot,w[maxn];
ll dis[maxn];
ll h[2],res[2][maxn];

inline void add(ll x,ll y,ll z){to[++tot]=y;next[tot]=head[x];head[x]=tot;w[tot]=z;}

inline void dfs1(ll x,ll fa,ll deep){	
	res[(deep&1)][(++h[(deep&1)])]=dis[x];
	for(R ll i=head[x],ver;i;i=next[i]){
		ver=to[i];
		if(ver==fa) continue;
		dis[ver]=w[i]-dis[x];
		dfs1(ver,x,deep+1);
	}
}
priority_queue<pair<ll,ll> >q;
ll ans;
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	n=read();k=read();
	for(R ll i=1,x,y,z;i<n;i++){
		x=read();y=read();z=read();
		add(x,y,z);add(y,x,z);
	}
	dfs1(1,-1,1);
	sort(res[0]+1,res[0]+h[0]+1);
	sort(res[1]+1,res[1]+h[1]+1);
	if(h[0]*h[1]<k){puts("Stupid Mike");return 0;}
	for(R ll i=1;i<=h[0];i++) q.push(make_pair(-(res[0][i]+res[1][1]),1));
	for(R ll i=1;i<k;i++){
		pair<ll,ll>rem=q.top();q.pop();
		if(rem.second<h[1]) q.push(make_pair(rem.first+res[1][rem.second]-res[1][rem.second+1],rem.second+1));
	}
	writeln(-q.top().first);
}
inline ll read(){ll x=0,t=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*t;}
inline void write(ll x){if(x<0){putchar('-');x=-x;}if(x<=9){putchar(x+'0');return;}write(x/10);putchar(x%10+'0');}
inline void writesp(ll x){write(x);putchar(' ');}
inline void writeln(ll x){write(x);putchar('
');}
原文地址:https://www.cnblogs.com/ylwtsq/p/13441168.html