1. 程式人生 > >HDU 6184&& 2017廣西邀請賽 Counting Stars(三元環計數)

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;
}