JZOJ 4320. 【NOIP2015模拟11.5】旅行

题目

思路

不想写了,直接使用

没错,关键就在求第 \(k\) 小的路径
上述提到堆的做法,我们可以用 \(STL\) 的优先队列来实现
只不过常数有点大~~~

\(Code\)

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
int n , k , h[N] , tot , cnt1 , cnt2;
LL a[N] , b[N];

struct edge{
	int to , nxt , w;
}e[N << 1];

struct node{
	int l , r;
	LL d;
	bool operator < (node c) const {return d > c.d;}
};

priority_queue<node> Q;

inline void add(int x , int y , int z)
{
	e[++tot].to = y;
	e[tot].w = z;
	e[tot].nxt = h[x];
	h[x] = tot;
}

inline void dfs(int x , int fa , int fl , LL D , int ad)
{
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa) continue;
		D = D + 1LL * fl * e[i].w;
		if (ad) a[++cnt1] = D;
		else b[++cnt2] = -D;
		dfs(v , x , -fl , D , ad ^ 1);
		D = D - 1LL * fl * e[i].w;
	}
}

int main()
{
	freopen("travel.in" , "r" , stdin);
	freopen("travel.out" , "w" , stdout);
	scanf("%d%d" , &n , &k);
	int u , v , w;
	for(register int i = 1; i < n; i++) 
	{
		scanf("%d%d%d" , &u , &v , &w);
		add(u , v , w) , add(v , u , w);
	}
	b[++cnt2] = 0;
	dfs(1 , 0 , 1 , 0 , 1);	
	sort(a + 1 , a + cnt1 + 1) , sort(b + 1 , b + cnt2 + 1);
	for(register int i = 1; i <= cnt1; i++) Q.push((node){i , 1 , a[i] + b[1]});
	node now;
	for(register int i = 1; i <= k; i++)
	{	
		if (Q.size())
		{
			now = Q.top() , Q.pop();
			if (i == k)
			{
				printf("%lld" , now.d);
				return 0;
			}
			if (now.r + 1 <= cnt2) Q.push((node){now.l , now.r + 1 , a[now.l] + b[now.r + 1]});
		}
		else{
			printf("Stupid Mike");
			return 0;
		}
	}
}

对于此类问题,我们还有一个很经典的做法
二分答案,然后判断路径组合中比这个答案小的能不能达到 \(k\)
后半句可以再套个二分实现

\(Code\)

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;

const int N = 1e5 + 5;
int n , k , h[N] , tot , cnt1 , cnt2;
LL a[N] , b[N];

struct edge{
	int to , nxt , w;
}e[N << 1];

inline void add(int x , int y , int z)
{
	e[++tot].to = y;
	e[tot].w = z;
	e[tot].nxt = h[x];
	h[x] = tot;
}

inline void dfs(int x , int fa , int fl , LL D , int ad)
{
	for(register int i = h[x]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == fa) continue;
		D = D + 1LL * fl * e[i].w;
		if (ad) a[++cnt1] = D;
		else b[++cnt2] = -D;
		dfs(v , x , -fl , D , ad ^ 1);
		D = D - 1LL * fl * e[i].w;
	}
}

inline int check(LL m)
{
	int l , r , mid , res , sum = 0;
	for(register int i = 1; i <= cnt1; i++)
	{
		l = 1 , r = cnt2 , res = 0;
		while (l <= r)
		{
			mid = (l + r) >> 1;
			if (b[mid] + a[i] <= m) res = mid , l = mid + 1;
			else r = mid - 1;
		}
		sum += res;
	}
	return sum >= k;
}

int main()
{
	freopen("travel.in" , "r" , stdin);
	freopen("travel.out" , "w" , stdout);
	scanf("%d%d" , &n , &k);
	int u , v , w;
	for(register int i = 1; i < n; i++) 
	{
		scanf("%d%d%d" , &u , &v , &w);
		add(u , v , w) , add(v , u , w);
	}
	b[++cnt2] = 0;
	dfs(1 , 0 , 1 , 0 , 1);	
	sort(a + 1 , a + cnt1 + 1) , sort(b + 1 , b + cnt2 + 1);
	if ((LL)cnt1 * cnt2 < k)
	{
		printf("Stupid Mike");
		return 0;
	}
	LL l = a[1] + b[1] , r = a[cnt1] + b[cnt2] , mid , res = l;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		if (check(mid)) res = mid , r = mid - 1;
		else l = mid + 1;
	}
	printf("%lld" , res);
}

实际上这两做法各有优劣
如果要求前 \(k\) 的话显然用堆,它的过程本质上就是取出了前 \(k\) 的数

posted @ 2020-08-05 18:35  leiyuanze  阅读(110)  评论(0编辑  收藏  举报