[NOIP2018]赛道修建

嘟嘟嘟


因为一些知道的人所知道的,不知道的人所不知道的原因,我来写今年的NOIP了。
现在看这题,心中满是疑问:我当时是多么的zz,这种水题为啥没做出来……


不管了,说正事。
先考虑部分分。
1.\(n \leqslant 15\)
不会。
2.\(m = 1\)
带权树的直径啊。树形dp一下维护最长链次长链即可。
3.菊花图。
最长的一组路径显然可能是由一条边或是任意两条边组成。
\(m <= \frac{n - 1}{2}\)的时候,贪心把前\(2m\)大的边最大的匹配最小的即可。
否则把最大的一些单独拎出来,剩下的两两匹配就完事了。
答案就是所有的max。
然后我考场上把这个写跪了……原因是第一种情况直接最大的匹配最小的,那中间的给扔了。(怒丢20分)
4.链
普及二分题。


这就是大众分55分啦。


至于二叉树咋回事,可以说是标程的弱化版。
也是二分,然后判断能否选出大于等于mid的m条路来。
然后对于每一个结点,分情况讨论:
1.如果左子树的链加边权大于等于mid,断掉作为一条新赛道。
2.右子树同理。
3.如果其中一棵子树的链加上边权比mid小,就贪心的把更长的链接到这个节点上,用来往上延伸。
没啦。


那么标程就很显然了。
对于结点\(u\),能在子树内匹配的就在子树内匹配,否则找一条最长的链延伸上去。
实现的时候每一层用一个multiset或vector+sort都行。


然后luogu卡vector(loj上过了),非得换成multiset。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<set>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 5e4 + 5;
inline ll read()
{
	ll ans = 0;
	char ch = getchar(), last = ' ';
	while(!isdigit(ch)) last = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(last == '-') ans = -ans;
	return ans;
}
inline void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

int n, m;
struct Edge
{
	int nxt, to, w;
}e[maxn << 1];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y, int w)
{
	e[++ecnt] = (Edge){head[x], y, w};
	head[x] = ecnt;
}

int a[maxn];
In void work0()		//菊花图 
{
	--n;
	for(int i = 0, j = 1; i <= ecnt; i += 2, ++j) a[j] = e[i].w;
	sort(a + 1, a + n + 1);
	if(m <= (n >> 1))
	{
		int L = n - (m << 1) + 1;
		int Min = INF;
		for(int i = L, j = 1; j <= m; ++i, ++j) Min = min(Min, a[i] + a[n - j + 1]);
		write(Min), enter;
	}
	else
	{
		int tp = n - m, Min = INF;
		for(int i = 1; i <= tp; ++i) Min = min(Min, a[i] + a[(tp << 1) - i + 1]);
		Min = min(Min, a[(tp << 1) + 1]);
		write(Min), enter;
	}	
}

In void dfs1(int now, int _f, int stp)
{
	for(int i = head[now], v; i != -1; i = e[i].nxt)
		if((v = e[i].to) ^ _f) a[stp] = e[i].w, dfs1(v, now, stp + 1);
}
In bool judge1(ll x)
{
	int cnt = 0;
	for(int i = 1, sum = 0; i < n; ++i) 
	{
		if(sum + a[i] >= x) ++cnt, sum = 0;
		else sum += a[i];
	}
	return cnt >= m;
}
In void work1()		//链 
{
	dfs1(1, 0, 1);		
	ll L = 0, R = (ll)5e9;
	while(L < R)
	{
		ll mid = (L + R) >> 1;
		if(judge1(mid))
		{
			if(L == mid) break;
			L = mid;
		}
		else 
		{
			if(R == mid - 1) break;
			R = mid - 1;
		}
	}
	write(L), enter;
}

ll dp[maxn], ans = 0;
In void dfs2(int now, int _f)
{
	ll Max1 = 0, Max2 = 0;
	for(int i = head[now], v; i != -1; i = e[i].nxt)
	{
		if((v = e[i].to) == _f) continue;
		dfs2(v, now);
		if(dp[v] + e[i].w > Max1) Max2 = Max1, Max1 = dp[v] + e[i].w;
		else if(dp[v] + e[i].w > Max2) Max2 = dp[v] + e[i].w;
	}
	ans = max(ans, Max1 + Max2);
	dp[now] = Max1;
}

In void work2()		//m = 1
{
	dfs2(1, 0);
	write(ans), enter;
}

int cnt = 0;
multiset<ll>::iterator pos;

multiset<ll> val[maxn];
In void dfs(int now, int _f, ll x)
{
	for(int i = head[now], v; i != -1; i = e[i].nxt)
	{
		if((v = e[i].to) == _f) continue;
		dfs(v, now, x);
		dp[v] += e[i].w;
		if(dp[v] >= x) ++cnt;
		else val[now].insert(dp[v]);
	}
	ll Max = 0;
	while(!val[now].empty())
	{
		if(val[now].size() == 1) {Max = max(Max, *val[now].begin()); val[now].erase(val[now].find(*val[now].begin())); break;}
		pos = val[now].lower_bound(x - *val[now].begin());
		if(pos == val[now].end())
		{
			Max = max(Max, *val[now].begin());
			val[now].erase(val[now].find(*val[now].begin()));
		}
		else
		{
			++cnt;
			val[now].erase(val[now].find(*pos));
			val[now].erase(val[now].find(*val[now].begin()));
			
		}
	}
	dp[now] = Max;
}
In bool judge(ll x)
{
	cnt = 0;
	dfs(1, 0, x);
	return cnt >= m;
}

int main()
{
	Mem(head, -1);
	n = read(), m = read();
	bool flg0 = 1, flg1 = 1;
	ll sum = 0;
	for(int i = 1; i < n; ++i)
	{
		int x = read(), y = read(), w = read();
		addEdge(x, y, w); addEdge(y, x, w);
		if(x ^ 1) flg0 = 0;
		if(y != x + 1) flg1 = 0;	
		sum += w;
	}
	if(flg0) {work0(); return 0;}
	if(flg1) {work1(); return 0;}
	if(m == 1) {work2(); return 0;}
	ll L = 0, R = sum;
	while(L < R)
	{
		ll mid = (L + R + 1) >> 1;
		if(judge(mid)) L = mid;
		else R = mid - 1;
	}
	write(L), enter;
	return 0;
}
posted @ 2019-01-31 21:29  mrclr  阅读(563)  评论(0编辑  收藏  举报