Description
月考刚考了年级倒数的敌敌来到电脑室,朝着正在写树套树套树套树的beginend大吼:我就不信我有辣么辣鸡!我可是去年普及组AC了第一题的人啊!
beginend不耐烦地扔了一道题给敌敌,然后说:你只要把这道题写出来你就不是辣鸡啦。
敌敌接过题一看,题目是这样的:
有n个点和n-1条无向边,满足任意两点都可以互相到达,求有多少条经过且仅经过三个点的简单路径。
(简单路径即为每个点最多被访问一次的路径)
但是敌敌发现自己并不会写这题,但又不愿承认自己是辣鸡这个事实,于是他就拿着题目来找刚刚成为保送狗的你。为了敌敌的智商,你只好选择了帮他一把。
Input
第一行一个正整数n表示城市数量。
接下来n-1行每行两个正整数u,v表示u和v之间有一条道路。
Output
输出一行,表示经过且仅经过三个点的简单路径的数量。
[数据范围与约定]
对于 30%的数据 n<=20
对于 60%的数据 n<=200
对于 100%的数据 n<=200000
Source
beginend
Analysis
幂痿大变态!!
明显是树图片,直接dfs,记录i节点的儿子个数
son[i][0]
和孙子个数
son[i][1]
,那么经过这个节点的路径条数为
∑son[i][1]+son[i][0]∗(son[i][0]−1)2
然后路径是双向的,要算两倍
良心数据没爆栈
Code
#include <iostream>
#define M 200001
using namespace std;
struct edge {int y,next;}e[M*2+1];
int son[M][2],ls[M*2+1],maxE=0,ans=0;
bool vis[M];
void add(int x,int y){e[++maxE]=(edge){y,ls[x]};ls[x]=maxE;}
void dfs(int x)
{
vis[x]=true;
for (int i=ls[x];i;i=e[i].next)
if (!vis[e[i].y])
{
dfs(e[i].y);
son[x][0]++;
son[x][1]+=son[e[i].y][0];
}
ans+=son[x][0]*(son[x][0]-1);
ans+=son[x][1]*2;
}
int main()
{
ios::sync_with_stdio(false);
int root=0,n;
cin>>n;
for (int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
if (!root)
root=x;
}
dfs(root);
cout<<ans<<endl;
return 0;
}