Counting Stars

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1146    Accepted Submission(s): 315

Problem Description

Little A is an astronomy lover, and he has found that the sky was so beautiful!
So he is counting stars now!
There are n stars in the sky, and little A has connected them by m non-directional edges.
It is guranteed that no edges connect one star with itself, and every two edges connect different pairs of stars.
Now little A wants to know that how many different "A-Structure"s are there in the sky, can you help him?
An "A-structure" can be seen as a non-directional subgraph G, with a set of four nodes V and a set of five edges E.
If V=(A,B,C,D) and E=(AB,BC,CD,DA,AC), we call G as an "A-structure".
It is defined that "A-structure" G1=V1+E1 and G2=V2+E2 are same only in the condition that V1=V2 and E1=E2.

Input

There are no more than 300 test cases.
For each test case, there are 2 positive integers n and m in the first line.

2≤n≤105, 1≤m≤min(2×105,n(n−1)2)

And then m lines follow, in each line there are two positive integers u and v, describing that this edge connects node u and node v.
1≤u,v≤n
∑n≤3×105,∑m≤6×105

Output

For each test case, just output one integer--the number of different "A-structure"s in one line.

Sample Input


4 5 1 2 2 3 3 4 4 1 1 3 4 6 1 2 2 3 3 4 4 1 1 3 2 4


Sample Output


1 6


Source

​2017ACM/ICPC广西邀请赛-重现赛(感谢广西大学) ​



        大致题意:给你一个图,然后找所有满足含有4个点5条边的子图有多少种。这里不同的点的定义是,只要有一条边不同,那么就是不同的子图。

        可以发现,满足这样的条件的子图,最后一定能够画成两个有公共边的三元环。那么我们的任务就是找这样的三元环。做法的话很容易知道是暴力,但是这个暴力并不是简单的暴力。我的想法是,枚举每一条边,然后对于这条边的两个端点,我分别拓展它们能够到达的点,求出公共点的数量t,这样能够组成的满足条件的三元环个数就是t*(t-1)/2.虽然理论的时间不复杂度不好计算,但是应该是可以的。但是结果确实TLE……

        然后至于正解还是不得不服暴力方法的巧妙。首先,我们考虑另外一个问题:如何求一个图的三元环。对于这个问题,我们肯定还是枚举每一条边,边然后找边上两端点能够到达的公共点。但是这里找公共点不是简单的枚举,而是要分类讨论。对于每一个点,我首先求一下度,然后根据这个度来分类讨论。


①统计每个点的度数

②入度<=sqrt(m)的分为第一类,入度>sqrt(m)的分为第二类

③对于第一类,暴力每个点,然后暴力这个点的任意两条边,再判断这两条边的另一个端点是否连接

因为m条边最多每条边遍历一次,然后暴力的点的入度<=sqrt(m),所以复杂度约为O(msqrt(m))

④对于第二类,直接暴力任意三个点,判断这三个点是否构成环,因为这一类点的个数不会超过sqrt(m)个,

所以复杂度约为O(sqrt(m)^3)=O(msqrt(m))

⑤判断两个点是否连接可以用set,map和Hash都行

       具体到这道题目,也是利用类似的方法。我们首先枚举边,其实相当于枚举点,枚举一个点x,然后选择与它相连的一条边,边的另一端作为第二个点y,与此同时保存下其他与点相连的边。然后我们判断y的度,如果y的度大于sqrt(m),那么相当于第二类点,于是我第三个点就在之前保存的与x相连的其他点中找公共点。这样就相当于找三元环的第二类对应的方法。如果y的度小于sqrt(m),那么相当于之前说的第一类,我们只需要再枚举y能到达的所有点即可。这样处理好公共点次数之后,最后贡献就是t*(t+1)/2。这样我们就可以计算出理论的时间复杂度O(N^1.5)。恰好可以通过这道题,具体见代码:

#include <bits/stdc++.h>

#define MAXN 100005
#define ll long long

using namespace std;

vector<int> e[MAXN];
bool mark[MAXN];
set<ll> st;
int link[MAXN];

ll cal(ll a) {
return a * (a - 1) / 2;
}

void init() {
memset(mark, false, sizeof(mark));
memset(link, 0, sizeof(link));
for (int i = 0; i < MAXN; i++) {
e[i].clear();
}
st.clear();
}

void addEdge(int u, int v) {
e[u].push_back(v);
}

int main() {
int n, m, u, v;
while (~scanf("%d %d", &n, &m)) {
init();
int b = sqrt(m);
for (int i = 0; i < m; i++) {
scanf("%d %d", &u, &v);
addEdge(u, v);
addEdge(v, u);
st.insert((ll)u * (n + 1) + v);
st.insert((ll)v * (n + 1) + u);
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
int x = i;
mark[i] = true;
for (int j = 0; j < e[x].size(); j++) {
link[e[x][j]] = x;
}
for (int j = 0; j < e[x].size(); j++) { //暴力枚举每一条边
int y = e[x][j];
ll sum = 0;
if (mark[y]) {
continue;
}
if (e[y].size() <= b) { //选择一个边少的进行枚举sqrt(m)
for (int k = 0; k < e[y].size(); k++) { //如果枚举的是y点的边,判断x是否有连边
int z = e[y][k];
if (link[z] == x) { //这边点集少,只需要跑m*sqrt(m),如果用set的话还要加上一个log(m)导致tle
sum++;
}
}
} else {
for (int k = 0; k < e[x].size(); k++) { //如果枚举的是x点的边,判断是否在set里
int z = e[x][k];
if (st.find((ll)z * (n + 1) + y) != st.end()) {
sum++;
}
}
}
ans += cal(sum);
}

}
printf("%lld\n", ans);
}
}