BanG Dream! It's MyGO!!!!!
题目描述
在“BanG Dream! It's MyGO!!!”的世界里,各个乐团的演出和排练场地像星星一样被连接在一起,形成了一张美丽的网络图。每个乐团都有自己独特的演出场地和练习室,这些地点通过各种路径互相连接,组成了一张复杂的图谱。
koala 作为一名热爱音乐的乐团忠实粉丝,突然有了一个灵感。他想为最喜欢的乐团设计一个独特的徽章,这个徽章需要从网络图中找出一些特别的图案来代表乐团,比如选三条边连接到一起,具体来说,他对以下三种图案感兴趣:
三角形:由三条边构成的连通子图,这是一种经典的图案。
三芒星:四个点形成的图案,一个点连向其他三个点。
闪电折线:一种特别的折线,由四个点按顺序连接,即一条链。
koala 想知道有多少种情况满足,你能帮帮他吗?
输入描述:
第一行包合两个整数 $n,m$ $(1 \leq n \leq 10^5, 1 \leq m \leq 2 \cdot 10^5)$,依次表示无向图的点数和边数;接下来 $m$ 行,每行两个整數 $u,v$ $(1 \leq u,v \leq n)$,表示一条边 $(u,v)$ 题目保证无重边、自环。
输出描述:
输出包含一个整数,表示你的答案。
示例1
输入
5 5
1 2
1 3
2 3
2 4
3 5
输出
8
说明
满足条件的导出子图的边集分别为:
(1,2),(1,3),(2,3)
(1,2),(2,3),(2,4)
(1,3),(2,3),(3,5)
(1,2),(1,3),(2,4)
(1,2),(1,3),(3,5)
(2,4),(2,3),(3,5)
(1,3),(2,3),(2,4)
(1,2),(2,3),(3,5)
备注:
给定一个无向图,你需要给出三条边的导出子图是连通的情况数量。
解题思路
先考虑简单的三芒星,枚举每个节点 $u$,那么以 $u$ 为中心的三芒星的数量就是 $C_{d_u}^{3}$,其中 $d_u$ 指 $u$ 的度数。
再考虑闪电折线,我们可以枚举中间的边 $(u, v)$,那么以边 $(u,v)$ 为中心的折线数量就是 $(d_u - 1) \cdot (d_v - 1)$。需要注意的是该统计方法会顺便把每个三角形都统计了 $3$ 次。
最后是三角形,就是统计图中三元组的数量,可以参考无向图三元环计数。
根号分治做法的 AC 代码如下,时间复杂度为 $O\left(\frac{nm}{w}\right)$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 3000;
int x[N], y[N];
vector<int> g[N];
bool vis[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> x[i] >> y[i];
g[x[i]].push_back(y[i]);
g[y[i]].push_back(x[i]);
}
LL ret = 0, s = 0;
map<int, bitset<N>> mp;
for (int i = 1; i <= n; i++) {
int s = g[i].size();
if (s >= 3) ret += s * (s - 1ll) * (s - 2) / 6;
if (s > M) {
for (auto &j : g[i]) {
mp[i][j] = 1;
}
}
}
for (int i = 1; i <= m; i++) {
ret += (g[x[i]].size() - 1ll) * (g[y[i]].size() - 1);
if (g[x[i]].size() > g[y[i]].size()) swap(x[i], y[i]);
if (g[x[i]].size() <= M) {
if (g[y[i]].size() <= M) {
for (auto &v : g[y[i]]) {
vis[v] = true;
}
for (auto &v : g[x[i]]) {
if (vis[v]) s++;
}
for (auto &v : g[y[i]]) {
vis[v] = false;
}
}
else {
auto &p = mp[y[i]];
for (auto &v : g[x[i]]) {
if (p[v]) s++;
}
}
}
else {
s += (mp[x[i]] & mp[y[i]]).count();
}
}
ret -= s / 3 * 2;
cout << ret;
return 0;
}
建 DAG 做法的 AC 代码如下,时间复杂度为 $O(m \sqrt{m})$:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 250;
int x[N], y[N];
int deg[N];
vector<int> g[N];
bool vis[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> x[i] >> y[i];
deg[x[i]]++, deg[y[i]]++;
}
LL ret = 0;
for (int i = 1; i <= n; i++) {
int s = deg[i];
if (s >= 3) ret += s * (s - 1ll) * (s - 2) / 6;
}
for (int i = 1; i <= m; i++) {
ret += (deg[x[i]] - 1ll) * (deg[y[i]] - 1);
if (deg[x[i]] < deg[y[i]] || deg[x[i]] == deg[y[i]] && x[i] < y[i]) g[x[i]].push_back(y[i]);
else g[y[i]].push_back(x[i]);
}
LL s = 0;
for (int i = 1; i <= n; i++) {
for (auto &j : g[i]) {
vis[j] = true;
}
for (auto &j : g[i]) {
for (auto &k : g[j]) {
if (vis[k]) s++;
}
}
for (auto &j : g[i]) {
vis[j] = false;
}
}
ret -= s * 2;
cout << ret;
return 0;
}
参考资料
2024河南萌新联赛第六场题解:https://blog.csdn.net/qq_45809243/article/details/141322896
标签:int,MyGO,ret,乐团,leq,BanG,long,Dream From: https://www.cnblogs.com/onlyblues/p/18374421