G - Gotta Catch Em All!(三元环)

题目: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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值