hdu 6184 Counting Stars(三元環計數)
題意:
給一張n個點m條邊的無向圖,問有多少個\(A-structure\)
其中\(A-structure\)滿足\(V=(A,B,C,D)\) && \(E=(AB,BC,CD,DA,AC)\)
顯然\(A-structure\)是由兩個有公共邊的三元環構成的
\(1 <=n <= 1e5\)
\(1 <= m <= min(2e5,n*(n-1)/2)\)
思路:
三元環計數
做法1、
①統計每個點的度數
②入度\(<=sqrt(m)\)的分為第一類,入度\(>sqrt(m)\)的分為第二類
③對於第一類,暴力每個點,然后暴力這個點的任意兩條邊,再判斷這兩條邊的另一個端點是否連接
因為\(m\)條邊最多每條邊遍歷一次,然后暴力的點的入度\(<=sqrt(m)\),所以復雜度約為\(O(msqrt(m))\)
④對於第二類,直接暴力任意三個點,判斷這三個點是否構成環,因為這一類點的個數不會超過\(sqrt(m)\)個,所以復雜度約為\(O(sqrt(m)^3)=O(msqrt(m))\)
⑤判斷兩個點是否連接可以用set,map和Hash都行,根據具體情況而行
這種做法建的是雙向邊,常數很大
更優的做法2、建有向邊 復雜度為\(O(msqrt(m))\)
對所有邊按照兩端點的度數定向,度數小的往度數大的走,度數相同則按編號小到大走,這樣定向后
可以保證是個有向無環圖。
為什么呢,要想定向存在環,則這個環上的點度數必須相同,由於保證了編號從小到大走
所以是不可能存在環的。
這樣定向同時還保證了每個點的出度不超過\(sqrt(m)\),很容易證明,如果存在一個點出度超過了\(sqrt(m)\),則說明存在其他\(sqrt(m)\)個點的度數\(>sqrt(m)\),算起來超過邊數\(m\)了。
對於這道題,我們在求三元環的時候,統計一下每條邊有多少對點能構成三元環,\(C(cnt,2)\)累計一下即可
做法1、
#include<bits/stdc++.h>
#define LL long long
using namespace std;
void read(int &x){
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
}
const int N = 1e5 + 10;
set<LL> g;
int deg[N];
vector<int> G[N];
int vis[N],vi[N];
int main(){
int n, m, u, v, Sz;
while(scanf("%d%d",&n,&m) != EOF){
Sz = sqrt(m + 0.5);
g.clear();
for(int i = 1;i <= n;i++){
vis[i] = vi[i] = deg[i] = 0;
G[i].clear();
}
for(int i = 0;i < m;i++){
scanf("%d%d",&u,&v);
g.insert(u + 1LL * v * n);
g.insert(v + 1LL * u * n);
deg[u]++,deg[v]++;
G[u].push_back(v);
G[v].push_back(u);
}
LL ans = 0;
for(int u = 1;u <= n;u++){
vis[u] = 1;
for(auto v:G[u]) vi[v] = u;
for(auto v:G[u]){
int cnt = 0;
if(vis[v]) continue;
if(deg[v] <= Sz){
for(auto vv:G[v]){
if(vi[vv] == u) cnt++;
}
}else{
for(auto vv:G[u]){
if(g.find(1LL * v * n + vv) != g.end()) cnt++;
}
}
ans += 1LL * cnt * (cnt - 1) / 2;
}
}
printf("%lld\n",ans);
}
return 0;
}
做法二、
#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
using namespace std;
void read(int &x){
x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
}
const int N = 1e5 + 10;
set<LL> g;
int deg[N];
vector<pair<int,int> > G[N];
int vi[N];
int X[N * 2],Y[N * 2],cnt[N],pos[N];
int main(){
int n, m, u, v, Sz;
while(scanf("%d%d",&n,&m) != EOF){
Sz = sqrt(m);
for(int i = 1;i <= n;i++){
vi[i] = deg[i] = pos[i] = 0;
G[i].clear();
}
g.clear();
int tot = 0;
for(int i = 0;i < m;i++){
scanf("%d%d",&X[i],&Y[i]);
u = X[i],v = Y[i];
deg[u]++,deg[v]++;
}
for(int i = 0;i < m;i++){
cnt[i] = 0;
if(deg[X[i]] < deg[Y[i]]) G[X[i]].push_back(make_pair(Y[i],i));
else if(deg[Y[i]] < deg[X[i]]) G[Y[i]].push_back(P(X[i],i));
else{
if(X[i] < Y[i]) G[X[i]].push_back(P(Y[i],i));
else G[Y[i]].push_back(P(X[i],i));
}
}
LL ans = 0;
for(int i = 0;i < m;i++){
u = X[i],v = Y[i];
for(auto vp:G[u]) pos[vp.first] = vp.second,vi[vp.first] = i + 1;
for(auto vp:G[v]){
int vv = vp.first;
if(vi[vv] == i + 1){
cnt[i]++;
cnt[pos[vv]]++;
cnt[vp.second]++;
}
}
}
for(int i = 0;i < m;i++) ans += 1LL * cnt[i] * (cnt[i] - 1) / 2;
printf("%lld\n",ans);
}
return 0;
}
