ZOJ 3863 Paths on the Tree

Description

Edward has a tree with n vertices conveniently labeled with 1,2,…,n.

Edward finds a pair of paths on the tree which share no more than k common vertices. Now Edward is interested in the number of such ordered pairs of paths.

Note that path from vertex a to b is the same as the path from vertex b to a. An ordered pair means (AB) is different from (BA) unlessA is equal to B.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains two integers nk (1 ≤ nk ≤ 88888). Each of the following n - 1 lines contains two integers aibi, denoting
an edge between vertices ai and bi (1 ≤ aibi ≤ n).

The sum of values n for all the test cases does not exceed 888888.

Output

For each case, output a single integer denoting the number of ordered pairs of paths sharing no more than k vertices.

Sample Input

1
4 2
1 2
2 3
3 4

Sample Output

path A paths share 2 vertices with A total
1-2-3-4 1-2, 2-3, 3-4 3
1-2-3 1-2, 2-3, 2-3-4 3
2-3-4 1-2-3, 2-3, 3-4 3
1-2 1-2, 1-2-3, 1-2-3-4 3
2-3 1-2-3, 1-2-3-4, 2-3, 2-3-4 4
3-4 1-2-3-4, 2-3-4, 3-4 3
93

Hint

The number of path pairs that shares no common vertex is 30.

The number of path pairs that shares 1 common vertex is 44.

The number of path pairs that shares 2 common vertices is 19.

这种题对于脑细胞的损耗实在有点大,wa了10发才过,实在是有点疲惫。

树分治,然后每个点统计边的种类,细节处理有点复杂。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef unsigned long long LL;
const int low(int x) { return x&-x; }
const int maxn = 3e5 + 10;
const int INF = 0x7FFFFFFF;
int T, n, m, x, y;

struct Tree
{
	int ft[maxn], nt[maxn], u[maxn], sz;
	int vis[maxn], mx[maxn], ct[maxn];
	LL d[maxn], D[maxn];

	void clear(int n)
	{
		mx[sz = 0] = INF;
		for (int i = 1; i <= n; i++) vis[i] = 0, ft[i] = -1;
	}
	void AddEdge(int x, int y)
	{
		u[sz] = y;	nt[sz] = ft[x];	ft[x] = sz++;
		u[sz] = x;	nt[sz] = ft[y]; ft[y] = sz++;
	}
	int dfs(int x, int fa, int sum)
	{
		int y = mx[x] = (ct[x] = 1) - 1;
		for (int i = ft[x]; i != -1; i = nt[i])
		{
			if (vis[u[i]] || u[i] == fa) continue;
			int z = dfs(u[i], x, sum);
			ct[x] += ct[u[i]];
			mx[x] = max(mx[x], ct[u[i]]);
			y = mx[y] < mx[z] ? y : z;
		}
		mx[x] = max(mx[x], sum - ct[x]);
		return mx[x] < mx[y] ? x : y;
	}
	int getdep(int x, int fa, int dep)
	{
		int ans = dep;
		for (int i = ft[x]; i != -1; i = nt[i])
		{
			if (u[i] == fa || vis[u[i]]) continue;
			ans = max(ans, getdep(u[i], x, dep + 1));
		}
		return ans;
	}
	LL get(int x, int fa, int dep)
	{
		LL cnt = 1, ans = 0;
		for (int i = ft[x]; i != -1; i = nt[i])
		{
			if (u[i] == fa) continue;
			LL y = vis[u[i]] ? mx[u[i]] : get(u[i], x, dep + 1);
			cnt += y;	ans += y*y;
		}
		D[dep] += cnt*cnt - ans;
		if (dep == 1) return ans;
		return cnt;
	}
	LL find(int x)
	{
		LL ans = 0, sum = 0, tot = 0;
		int len = getdep(x, -1, 1);
		if (len + len <= m + 1) return 0;
		sum = get(x, -1, 1);
		for (int i = 1; i <= len; i++) d[i] = 0;
		for (int i = ft[x]; i != -1; i = nt[i])
		{
			if (vis[u[i]]) continue;
			int y = getdep(u[i], x, 2);
			for (int j = 2; j <= y; j++) D[j] = 0;
			LL z = get(u[i], x, 2);
			for (int j = 2; j <= y; j++)
			{
				LL s = 0;
				for (int k = min(m + 1 - j, len); k > 0; k -= low(k)) s += d[k];
				ans += D[j] * (tot - s);
			}
			for (int j = 2; j <= y; j++)
			{
				for (int k = j; k <= len; k += low(k)) d[k] += D[j];
				if (j > m) ans += (((LL)n - z)*((LL)n - z) + z*z - sum)*D[j];
				tot += D[j];
			}
		}
		return ans;
	}
	int dfs(int x,int fa)
	{
		int cnt=1;
		for (int i=ft[x];i!=-1;i=nt[i])
		{
			if (u[i]==fa) continue;
			cnt+=vis[u[i]]?mx[u[i]]:dfs(u[i],x);
		}
		return cnt;
	}
	LL work(int x, int sum)
	{
		int y = dfs(x, -1, sum);
		LL ans = find(y);	vis[y] = 1;
		for (int i = ft[y]; i != -1; i = nt[i])
		{
			if (vis[u[i]]) continue;
			mx[y] = n-dfs(u[i],y);
			ans += work(u[i], ct[u[i]] > ct[y] ? sum - ct[y] : ct[u[i]]);
		}
		return ans;
	}
}solve;

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &n, &m);
		LL ans = (LL)n * (n + 1) >> 1;
		solve.clear(n);
		for (int i = 1; i < n; i++)
		{
			scanf("%d%d", &x, &y);
			solve.AddEdge(x, y);
		}
		printf("%llu\n", ans*ans - solve.work(1, n));
	}
	return 0;
}
时间: 2024-10-18 02:33:12

ZOJ 3863 Paths on the Tree的相关文章

ZOJ 3863 Paths on the Tree 树分治

题目链接:点击打开链接 题意: 给定n个点的树. 常量k 问:对于一对路径,如果公共点<=k则为合法. 问有多少个合法的路径. {1-3, 2-4} 和 {2-4,1-3} 视为2个不同的路径对. 1-3, 3-1视为相同路径. 思路: 首先来得到一个O(n^3)的算法: 把问题转成=> 总方案数 - 公共点>k个的路径对数 显然公共点是连续的,所以公共点会组成一条路径,我们设为 x-y,则枚举x和y,就能得到公共的部分(当然要保证x-y的公共点数>k) 那么现在的问题是 以公共路

hdu4912 Paths on the tree --- LCA贪心

给一棵n个结点的树,m条路径的起点和终点, 问至多可以选择多少条路径使其两两间没有公共点. 这题的主要问题是, 1.如何判断两条路径上没有交点 2.按什么策略来选 看上去感觉是最大匹配问题,但nm的范围较大问题1无法高效的解决. 画个图发现可能和LCA有关,但比赛时不知道这到底有什么用,完全没想贪心. 要选择尽量多,就是要尽量避免冲突. 我们选择一个点作为根,把给的边画出来建树就可以发现,尽量选深度大的路径可以使冲突尽量小. 于是把路径按LCA深度由大到小排序,依次和之前不冲突就可以选. 下面就

hdu 4912 Paths on the tree(树链剖分+贪心)

题目链接:hdu 4912 Paths on the tree 题目大意:给定一棵树,和若干个通道,要求尽量选出多的通道,并且两两通道不想交. 解题思路:用树链剖分求LCA,然后根据通道两端节点的LCA深度排序,从深度最大优先选,判断两个节点均没被标 记即为可选通道.每次选完通道,将该通道LCA以下点全部标记. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include

HDU 4912 Paths on the tree

http://acm.hdu.edu.cn/showproblem.php?pid=4912 题意:给一棵树,再给一些路径,求最多有多少条路径不相交. 题解:主要是贪心的想法.用LCA处理出路径的层数,然后从最深处的节点往上找.因为节点越深,对其他路径影响度越小,相交的可能性越低.需要手动扩栈. 1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <iostream> 3 #include

HDU 4912 Paths on the tree LCA 排序贪心

lca... 排个序然后暴力保平安 _(:зゝ∠)_ #pragma comment(linker, "/STACK:102400000,102400000") #include"cstdio" #include"iostream" #include"set" #include"queue" #include"string.h" using namespace std; #define

HDU 4912 Paths on the tree(LCA+贪心)

题目链接 Paths on the tree 来源  2014 多校联合训练第5场 Problem B 题意就是给出m条树上的路径,让你求出可以同时选择的互不相交的路径最大数目. 我们先求出每一条路径(u, v)中u和v的LCA:w,按照路径的w的深度大小deep[w]对所有的路径排序. deep[w]越大,排在越前面. 然后从第一条路径开始一次处理,看c[u]和c[v]是否都没被标记过,如果都没被标记过则我们把这条路径选上,把答案加1. 同时标记以w为根的子树的节点为1,方便后续对c数组的查询

hdu 4912 Paths on the tree(树链拆分+贪婪)

题目链接:hdu 4912 Paths on the tree 题目大意:给定一棵树,和若干个通道.要求尽量选出多的通道,而且两两通道不想交. 解题思路:用树链剖分求LCA,然后依据通道两端节点的LCA深度排序,从深度最大优先选.推断两个节点均没被标 记即为可选通道. 每次选完通道.将该通道LCA下面点所有标记. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #includ

Codeforces Round #615 (Div. 3) F. Three Paths on a Tree

F. Three Paths on a Tree 原题链接:https://codeforces.com/contest/1294/problem/F 题目大意: 给定一棵树,选出三点,使三点连成的j简单路径最大.简而言之,三个点连成的边的集合大小. 解题思路: 假设任取一点为三点连线的公共点,最长路径就是这个点到其他三个点的三条最长边之和,可知这个点一定在直径上(画图分析假设不在时的最长路径可反证).所以先求出树的直径,在使用$ans =(a b+a c+b c) / 2$遍历可以得到第三个点

树的直径| CF#615Div3 F. Three Paths on a Tree

F. Three Paths on a Tree 思路 两种方法: 1.两次bfs求树的直径,顺便求出一个直径端点到所有点的最短距离:再bfs一次,求另一个直径上的端点到其它所有点的最短距离:之后枚举第三个端点(不等于端点1和端点2),dis(a,b) + dis(b,c) + dis(a,c) 再除以 2 就是最终答案,因为每个路径走了两次所以除以2. 2.dfs求树的直径,记录直径上的所有点.从直径上的所有点去搜索它们到不在直径上的点的最远距离.最后直径+这个最远距离就是答案 代码1 bfs