[coci2015-2016 coii] torrent【树形dp 二分】

传送门:http://www.hsin.hr/coci/archive/2015_2016/

进去之后点最下面那个。

这道题没有想出来,可惜了,其实不难的。

题目是两个“源”的,我们先考虑单源的问题。先把这个源拉成树根,然后设f(i)为以结点i为树根的子树,全部收到文件所需要的时间,由于同一时间,结点i只能向其中一个子结点传送文件,那么假设son(i, j)表示结点i的第j个子结点(程序中不需要这个数组,这里只是为了叙述方便),那么f(i) = max{ j + f( son(i, j) )}。为了让这个max值最小,很显然需要把f( son(i, j) )按照从大到小排,即子树所需时间越长的,结点i就应该越早传文件给它。这样子就解决了单源的问题。

那么再考虑双源的。考虑从a到b的路径,若a到b的路径上,存在相邻的c,d两个点,很显然,在最佳方案里一定会有文件从a传到c,从b传到d,所以,如果把(c, d)这条边断开也不会影响到答案,这就变成了求两次单源的问题。因此,若把a到b的路径上的边按离a从远到近编号,设f_a(i)表示断开边i后,a所属的那棵树所需要的时间,设f_b(i)表示断开边i后,b所属的那棵树所需要的时间,那么最终答案ans =  min{ max{f_a(i), f_b(i)} }。

这样会tle(我会说std有一个点,在我们学校的垃圾电脑上跑了92s吗?时限可是2s啊喂!你这标程怎么写的!),因此需要优化。很显然(又是显然),断开的边i离a越远,f_a(i)就会越大(其实也可以取等),f_b(i)就会越小(其实也可以取等),因此可以再二分一下断哪条边,就ok了!复杂度是O(n * (logn)^2)

p.s.主函数的前三行是开栈,以免递归爆栈。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

const int maxn = 300005;

int n, a, b, t1, t2;
int head[maxn], to[maxn << 1], next[maxn << 1], lb, fa[maxn];
char mark[maxn];
std::vector<int> fson[maxn];
struct edge {
	int u, v;
} e[maxn];
int idx;

inline void ist(int aa, int ss) {
	to[lb] = ss;
	next[lb] = head[aa];
	head[aa] = lb;
	++lb;
}
void dfs(int r, int p) {
	fa[r] = p;
	for (int j = head[r]; j != -1; j = next[j]) {
		if (to[j] != p) {
			dfs(to[j], r);
		}
	}
}
int get_ans(int r, int p, int mar) {
	fson[r].clear();
	int son_num = 0, mx = 0;
	for (int j = head[r]; j != -1; j = next[j]) {
		if (to[j] != p && mark[to[j]] != -mar) {
			fson[r].push_back(get_ans(to[j], r, mar));
			++son_num;
		}
	}
	std::sort(fson[r].begin(), fson[r].end());
	for (std::vector<int>::iterator it = fson[r].begin(); it != fson[r].end(); ++it) {
		mx = std::max(mx, *it + son_num);
		--son_num;
	}
	return mx;
}

int main(void) {
	int size = 256 << 20; // 256MB
	char *p = (char*)malloc(size) + size;
	__asm__("movl %0, %%esp\n" :: "r"(p));
	freopen("torrent.in", "r", stdin);
	freopen("torrent.out", "w", stdout);
	memset(head, -1, sizeof head);
	memset(next, -1, sizeof next);
	scanf("%d%d%d", &n, &a, &b);
	for (int i = 1; i < n; ++i) {
		scanf("%d%d", &t1, &t2);
		ist(t1, t2);
		ist(t2, t1);
	}

	dfs(a, 0);
	for (int i = b; i != a; i = fa[i]) {
		e[idx++] = (edge){fa[i], i};
	}

	int left = 0, right = idx - 1, mid;
	while (left < right) {
		mid = (left + right) >> 1;
		mark[e[mid].u] = -1;
		mark[e[mid].v] = 1;
		if (get_ans(a, 0, -1) > get_ans(b, 0, 1)) {
			left = mid + 1;
		}
		else {
			right = mid;
		}
		mark[e[mid].u] = mark[e[mid].v] = 0;
	}
	if (!left) {
		mark[e[0].u] = -1;
		mark[e[0].v] = 1;
		printf("%d\n", get_ans(b, 0, 1));
	}
	else {
		int ans1 = -666, ans2 = -666;
		mark[e[left].u] = -1;
		mark[e[left].v] = 1;
		ans1 = get_ans(b, 0, 1);
		mark[e[left].u] = mark[e[left].v] = 0;

		mark[e[left - 1].u] = -1;
		mark[e[left - 1].v] = 1;
		ans2 = get_ans(a, 0, -1);
		printf("%d\n", std::min(ans1, ans2));
	}
	return 0;
}

  

时间: 2024-10-08 13:51:36

[coci2015-2016 coii] torrent【树形dp 二分】的相关文章

hdu3586 树形dp+二分求解

http://acm.hdu.edu.cn/showproblem.php?pid=3586 Problem Description In the battlefield , an effective way to defeat enemies is to break their communication system. The information department told you that there are n enemy soldiers and their network w

hdu 3586 树形dp+二分

题目大意:给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵 树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线.现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所 有前线与司令部联系所花费的总费用少于m时的最小limit.1<=n<=1000,1<=m<=100万 题目要问的是最小的最大限制,必然二分答案 然后对于每一个值,树形DP判定是否可行 dp[i]表示要切断以i为根的其它所有子树的最小代价. 其中设定叶子结点的代价为无穷大

[hdu3586]Information Disturbing树形dp+二分

题意:给出一棵带权无向树,以及给定节点1,总约束为$m$,找出切断与所有叶子节点联系每条边所需要的最小价值约束. 解题关键:二分答案,转化为判定性问题,然后用树形dp验证答案即可. dp数组需要开到ll,如果用设inf的解法. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=1e6+7; 5 const int inf=0x3f3f3f3f; 6 struct e

UvaLive 6534 Join two kingdoms 树形DP+二分

链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4545 题意:两个国家A,B,分别有N座城市和Q座城市(1 ≤ N, Q ≤ 4 × 10^4),每个国家里的城市都是树形结构,每条边的权值都是1.现在要随机从两个国家中各选择一个城市来将两个国家连接起来,问连接起来的大国家里面的最长路的期望是多少. 思路:首先用树形DP

HDU 3586 Information Disturbing 树形DP+二分

Information Disturbing Problem Description In the battlefield , an effective way to defeat enemies is to break their communication system.The information department told you that there are n enemy soldiers and their network which have n-1 communicati

hdu3586 树形dp+二分答案

/* dp[i]表示孤立i结点的费用,二分功率上限w,即dp[i]在选择时不可以选择功率大于w的边 */ #include<bits/stdc++.h> using namespace std; #define maxn 1050 struct Edge{int to,nxt,w;}edge[maxn<<1]; int x,flag[maxn],dp[maxn],tot,head[maxn],n,m; void init(){ tot=0; memset(head,-1,sizeo

HDU3585 Information Disturbing 树形dp+二分

http://acm.split.hdu.edu.cn/showproblem.php?pid=3586 题意 : 给定一个带权无向树,要切断所有叶子节点和1号节点(总根)的联系,每次切断边的费用不能超过上限limit,问在保证总费用<=m下的最小的limit. 显然最小的总费用和最小的limit不能同时维护,那么只能在dfs中维护一个然后另一个用特殊的(朗诵)技巧解决-- emmmmmmmmm--说白了就是二分求最小的limit,然后就没有下面了. 算是普通难度,只要知道二分就很好写,虽然我开

Codeforces 627D Preorder Test(二分+树形DP)

题意:给出一棵无根树,每个节点有一个权值,现在要让dfs序的前k个结点的最小值最大,求出这个值. 考虑二分答案,把>=答案的点标记为1,<答案的点标记为0,现在的任务时使得dfs序的前k个节点都为1. 考虑树形DP. 用dp[u]表示从节点u开始在子树中进行dfs最多可以经过多少个为1的结点,显然,若某一个子树中节点全为1,那么这个可以加到dp[u]中,此外还可以在不全为1的子树中挑选一个加到dp[u]上. 那么答案就是从标记为1的节点当做根,选两颗不完全子树和所有的完全子树(包括从父亲向上的

HDU 3586 Information Disturbing(二分+树形dp)

http://acm.split.hdu.edu.cn/showproblem.php?pid=3586 题意: 给定一个带权无向树,要切断所有叶子节点和1号节点(总根)的联系,每次切断边的费用不能超过上限limit,问在保证总费用<=m下的最小的limit. 思路: 对于上限limit我们可以二分查找.然后就是树形dp,看代码就可以理解的. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring>