Y
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 2865 Accepted Submission(s): 835
Problem Description
Sample Input
4 1 2 1 3 1 4
Sample Output
1
Hint
1. The only set is {2,3,4}. 2. Please use #pragma comment(linker, "/STACK:16777216")
Source
2013 Multi-University Training Contest 10
Recommend
zhuyuanchen520 | We have carefully selected several similar problems for you: 6437 6436 6435 6434 6433
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)
#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
ll gcd(ll x,ll y) { return y==0?x:gcd(y,x%y); }
const int maxn =1e5+5;
const int mod=1e9+7;
/*
题目大意:找出树中三点对对数,
满足一条简单路径不覆盖这三点对。
这道题明显的树形DP计数的味道在里面,
正难则反,我在思考找符合题意的三点对时也是踩了不少坑,
主要打破我幻想的一个例子就是,可以存在两个点在一个子树中,
另一个点在子树外面的情况,这样我的想法就有漏洞。
正难则反,我们考虑一条路径能覆盖的三点对,a,b,c,
在遍历根节点时,假设一个节点在根节点上,另一个点在当前枚举的子树中,
那么如何完全不重复的统计这类情况的个数呢?
在更新sons[u]的过程中,sons[u]同时也是子树节点数量的前缀和,
所以用总结点数减去该前缀和就得到未和当前子树配对的节点数量,
注意这里并没有重复计算,因为之前计算过的肯定不会与当前节点配对,
在总数中已经减去了。
*/
int n,x,y;
///链式前向星
struct node
{
int u,nxt;
};
node edge[maxn<<2];
int head[maxn],tot=0;
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void add(int x,int y)
{
edge[tot]=node{y,head[x]};
head[x]=tot++;
}
ll ans,sons[maxn];///计数
void dfs(int u,int pre,int dis)
{
sons[u]=1;
for(int i=head[u];~i;i=edge[i].nxt)
{
int v=edge[i].u;
if(v==pre) continue;
dfs(v,u,dis+1);
sons[u]+=sons[v];
ans+=1LL*(n-sons[u])*sons[v];
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
init();
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
ans=0;
memset(sons,0,sizeof(sons));
dfs(1,-1,0);
ll tot=1LL*n*(n-1)*(n-2)/6;
printf("%lld\n",tot-ans);
}
return 0;
}
/*
6
1 2
1 3
3 4
3 5
1 6
4 6
4 7
5 8
5 9
*/