您的位置:首页 > 其它

HDU 4705 Y 多校训练第十场1010

2013-10-12 14:30 465 查看
题意就是在一棵树上找三个点,使得没有一条简单路径包含这三个点。

我们换个角度来想,所有方案很好求,n个里选3个的组合数, 然后我们减去在一条简单路径上的三点对的个数。于是问题转化成求这样的三点对,我是这么想的,可以枚举2个点, 把不符合要求的第三个点找到,然后因为树上的两点有唯一的简单路径相连, 于是我们就可以知道有多少个点是不能作为第三个点的点了。个数恰好等于两端这两个点之间的距离减1。(每条边权值为1,因此这个答案应该很好理解,假设简单路径是 A,X,Y,Z,B,距离4,有三个点不能作为C)。于是我们就把问题转化成求任意两个节点之间的距离了,也就是以一个节点为根,其他节点到他的距离之和,这样问题基本解决,显然树DP,我们考虑一下转移过程需要的信息,假设已经知道某个节点A,以他为根时其他节点到他的距离之和,那么DFS转移到他的子节B点时,我们会发现有些点距离加了1,有些点距离减了1,加一的点是除了B及B的子树以外的节点,而减一的是B的子树上的点,于是我们需要每个节点的子树节点数目的信息。其他信息可以不要,当然可以开一个数组记录以每个节点为根的时候的距离和,但是没有必要,我们直接把他加到答案dis上去就可以了。所以树DP就这样进行下去:先找一个节点,做一遍DFS,知道了他到所有其他节点的距离之和,这个有用,同时我们保存每个节点的子树的节点数信息。然后开始第二遍DFS,考虑距离和这个量如何转移:A的dis和为sum,他的子节点B的子树有s[B]个节点(包括本身),那么
n - s[B] 个点距离加1,B的子树上的点距离减1,s[B](注意B原来对A贡献了1,现在不贡献所以B也算在减一的节点里面),于是以B为根的树的距离和就是 sum + n - s[B] * 2, 最后还有一些小细节,就是我们求的是距离之和,没有减1,应该减多少个1呢?我们对每个点都让他做了一次根节点,其他作为他的子节点有n - 1个所以应该减去(n - 1) * n,最后我们注意到我们重复计算了答案,因为路径A--B 与B--A是一样的,所以答案除以2。到此这题解决。

表示我是弱菜,以前根本不会树DP,这是A的第一题,希望不要鄙视,还有数的存储用的是数组模拟的领接表,以前也不会,队友教的,所以把这个学了好多东西的题写篇blog记录下来!

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define sl(a) strlen(a)
#define LL long long
#define dou double
const LL Mod = 1000000007;
const LL N = 100005;
using namespace std;

LL f
, e[2 * N], ne[2 * N], s
, dis, ans, tot, n;

void add(LL x, LL y){
ne[++tot] = f[x], f[x] = tot, e[tot] = y;
}

void dfs(LL x, LL p, LL dep){
dis += dep;
for (LL i = f[x]; i; i = ne[i]){
if (e[i] != p){
dfs(e[i], x, dep + 1);
s[x] += s[e[i]];
}
}
}

void dfs2(LL x, LL p, LL sum){
//cout << x << ' ' << sum << endl;
ans += sum;
for (LL i = f[x]; i; i = ne[i]){
if (e[i] != p){
dfs2(e[i], x, sum + n - 2 * s[e[i]]);
}
}
}

void init(){
mem(f), mem(ne), mem(e);
for (LL i = 1; i <= N ;++i) s[i] = 1;
dis = tot = ans = 0;
}

int main()
{
/*freopen("in.txt", "r", stdin);
freopen("out.txt", "w",stdout);*/
//LL size = 256 << 20; // 256MB
//   char *p = (char*) malloc(size) + size;
//   __asm__("movl %0, %%esp\n" :: "r"(p));
ios :: sync_with_stdio(false);
LL x, y;
while (cin >> n){
init();
for (LL i = 0; i < n - 1; ++i){
cin >> x >> y;
add(x, y), add(y, x);
}
dfs(1, -1, 0);
//cout << dis << endl;
//for (LL i = 1; i <= n; ++i) cout << i << ' ' << s[i] << endl;
dfs2(1, -1, dis);
ans = ans - n * (n - 1);
cout << (n - 1) * (n - 2) * n / 6 - (ans / 2) << endl;
}

return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp