题目:https://vjudge.z180.cn/contest/441265#problem/G
题意:题目给n个点,n-1条无向边,是个图,不一定是树,可能有环和孤立点,,,,求从i点到j点的最短步数是2的所有路径。
题解:找到每个点的度数du,即它连边的数量,然后C(du,2)就是以这个点为中间点的所有情况,,,但如果存在三元环,i到j的最短路径就是1而不是2,所以要把三元环的情况减掉,题目就变成了找三元环的个数,,这里把无向边直接转为有向边就行,最后答案乘以2,,这里必须要有的一个优化就是,存图的时候,往度数小的那个点存图,这样会让整个图每个点后面连的其它点更加均匀化,这样下面暴力解的时候就不会出现那种极限情况令代码超时。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int N = 3e5 + 10;
vector<int> g[N];
// map<pair<int, int>, int> st;
int in[N]; //存度数
P e[N]; //存有向边
map<pair<int, int>, int> ma; //去重
int v[N];
int main() {
ios::sync_with_stdio(false);
int n;
cin >> n;
int idx = 0;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
if (u > v) swap(u, v); //小的放前面
if (ma.count({u, v})) continue; //去重
ma[{u, v}] = 1;
e[idx++] = make_pair(u, v);
}
// sort(e, e + idx);
// idx = unique(e, e + idx) - e;
for (int i = 0; i < idx; i++) { //获取每个点的读书
int x = e[i].first, y = e[i].second;
in[x]++, in[y]++;
}
for (int i = 0; i < idx; i++) {
int x = e[i].first, y = e[i].second;
if (in[y] > in[x]) //把边赋给度数小的点,即让每个点后面连的边均匀化
g[x].emplace_back(y);
else
g[y].emplace_back(x);
}
int num = 0;
LL ss = 0;
for (int i = 1; i <= n; i++) {
ss += (LL)in[i] * (in[i] - 1) / 2; //获取以这个点为中间点的,所有情况,组合数C(in,2)
for (auto it : g[i]) { //扫一遍连i的点,记录这些点连向i
v[it] = i;
}
for (auto it : g[i]) { //枚举这些点
for (auto iit : g[it]) { //再扫这些点连着的点
if (v[iit] == i) num++; //看看这些点是不是上面找出的it,是的话就是一个三元环
}
}
}
ss = (LL)ss * 2 - num * 6; //把三元环的构成的减掉
cout << ss;
return 0;
}
/*
7
1 2
1 3
1 4
2 3
3 4
2 4
*/