HDU 6184&& 2017廣西邀請賽 Counting Stars(三元環計數)
CountingStars
Time Limit: 4000/2000 MS(Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 309 Accepted Submission(s):82
Problem Description
Little A is an astronomy lover, and he hasfound 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 mnon-directional edges.
It is guranteed that no edges connect one star with itself, and every two edgesconnect different pairs of stars.
Now little A wants to know that how many different "A-Structure"s arethere in the sky, can you help him?
An "A-structure" can be seen as a non-directional subgraph G, with aset 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 oneinteger--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
題目意思是說,在一個給定的無向圖中,找"A-structure"的個數。
"A-structure"這個題目有解釋,V=(A,B,C,D) and E=(AB,BC,CD,DA,AC)
這樣的一個點集和邊集就是符合條件的。
開始沒太看懂題目,僵化地認為必須是一個一個由四個點組成的環+一條對角線
這樣怎麼也解釋不了第二個樣例。
只好轉頭在看一遍題目。
其實題目叫我們找的是兩個三元環,這兩個三元環共用一條邊。
比如說V=(A,B,C,D) and E=(AB,BC,CD,DA,AC)這個裡面是
三元環ABC和三元環ADC共用了AC。
樣例二給的是一個完全圖,一共有六條邊。這六條邊,每一條邊都可以作為像上面AC一樣的共邊。,都能找到兩個三元環。
這樣,這道題目其實就用簡單的方法就可以過。
我們每次列舉一條邊,看它是否能作為共邊,如果它是,那數有多少個點能夠和這條邊構成三元環。最後的答案就是C(cnt,2),(這符合條件的點中任選兩個都符合A-structure)
比如說樣例二,開始判斷AB是否滿足條件。發現它滿足,C和D都可以與AB邊構成三元環,所以以AB為共邊的A-structure有C(2.2)個,也就是一個,其他5條邊也是類似。
這樣的確實能夠解決問題,但是我們估計一下演算法複雜度,發現時間不夠。
所以我們需要優化一下。
我們有兩種方法來列舉(具體看程式碼)
一種是列舉X,一種是列舉Y,
為了降低演算法複雜度,我們需要根據兩個點集的大小來選擇哪種方式
這時候,我們可以選擇以Sqrt(m)作為為一個分界點,具體可看程式碼
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<string>
#include<stdio.h>
#include<map>
#include<vector>
#include<deque>
#include<algorithm>
#define maxn 100005
using namespace std;
map<pair<int,int>,int> maps;
vector<int> p[maxn];
int outdeg[maxn],link[maxn],vis[maxn];
int n,m;
long long ans;
void init()
{
ans=0;
maps.clear();
memset(outdeg,0,sizeof (outdeg));
memset(link,0,sizeof (link));
memset(vis,0,sizeof (vis));
for(int i=1;i<=n;i++)
p[i].clear();
}
int main()
{
int i,j,k;
int x,y,z;
int t,a,b,c;
long long sum;
pair<int,int> tmp1,tmp2;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();//清空
t=sqrt(m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
p[a].push_back(b);//儲存雙向邊
p[b].push_back(a);
outdeg[a]++; outdeg[b]++;//統計每個點的度數
tmp1=make_pair(a,b);//把邊加入Map裡面
tmp2=make_pair(b,a);//方便後面查詢
maps[tmp1]=1;//用set,Hash都可以,只要後面能找到邊即可
maps[tmp2]=1;
}
for(i=1;i<=n;i++)//開始列舉
{
x=i;
vis[x]=1;
for(j=0;j<p[x].size();j++)
{
y=p[x][j];
link[y]=x;//與之相連的邊標記出來
}
for(j=0;j<p[x].size();j++)
{
sum=0;
y=p[x][j];//這裡比較重要,此時我們選擇的邊是XY
if(vis[y])//每個點列舉一次
continue;
if(outdeg[y]<=t)//分界點
{
for(k=0;k<p[y].size();k++)
{
z=p[y][k];//此時選擇YZ邊
if(link[z]==x)//如果XZ相連
sum++;//可構成三元環,計數
}
}
else
{
for(k=0;k<p[x].size();k++)
{
z=p[x][k];//此時XZ相連
pair<int,int> tmp=make_pair(z,y);
if(maps[tmp]!=0)//如果ZY相連
sum++;//可構成三元環,計數
}
}
ans+=(sum*(sum-1))/2;//最後答案是C(cnt,2);
}
}
printf("%lld\n",ans);
}
return 0;
}