HDU6184 Counting Stars

题意:

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.

数据范围:

n ≤ 1 0 5 , m ≤ 2 ∗ 1 0 5 n\leq{10^5},m\leq{2*10^5} n105,m2105

Analysis:

翻译不是这题的重点,我们来讲讲这题真正要求的东西,其实就是懒得翻译,英文不好。
事实上就是让我们三元环计数,找出一条边存在于多少个三元环中。
我们考虑一种暴力:枚举一个点,枚举所有出边的点打上标记,再枚举出边,然后枚举出边的点的出边,看出边的点是否有标记,语文功底太差,超级绕 。我们设一个点的出度为 o u t v out_v outv,不难发现这样的复杂度为 ∑ e u , v o u t u + o u t v \sum_{e_{u,v}}out_u+out_v eu,voutu+outv。让每条边只会被枚举两次。
考虑平衡出度,我们强制让度数小的连向度数大的点(有向边),度数相同按编号有序连边。
不难发现会是一张 D a g Dag Dag,因为如果有环的话,编号的偏序关系会矛盾。原先的三元环仍然可以如上枚举,且仅会被枚举一次,因为必定会有且仅有一个点入度为 2 2 2
这样复杂度变为 ∑ e u , v o u t v \sum_{e_{u,v}}out_v eu,voutv,那么每个点出度为多少呢,是不会超过 m \sqrt{m} m 的。
考虑反证:若一个点出度超过 m \sqrt{m} m ,因为连出去的点出度都大于其,所以至少 m \sqrt{m} m 个点,每个点至少 m \sqrt{m} m 条出边,出边总数大于 m m m ,所以与条件矛盾。
那么总复杂度为 O ( m m ) O(m\sqrt{m}) O(mm )。比较优秀,常数也比较小。
至于统计答案,只要算出每条边存在于多少个三元环,然后 C c n t 2 C_{cnt}^2 Ccnt2加起来就好。

Code:

# include
# include
# include
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
int st[N],to[N << 1],nx[N << 1],id[N << 1];
int vis[N][2],D[N],e[N << 1][2],cnt[N << 1];
int n,m,tot;
inline void add(int u,int v,int w)
{ to[++tot] = v,nx[tot] = st[u],id[tot] = w,st[u] = tot; }
int main()
{
	while (~scanf("%d%d",&n,&m))
	{
		memset(vis,0,sizeof(vis)),memset(st,0,sizeof(st)),memset(cnt,0,sizeof(cnt)),memset(D,0,sizeof(D)),tot = 0;
		for (int i = 1 ; i <= m ; ++i) scanf("%d%d",&e[i][0],&e[i][1]),++D[e[i][0]],++D[e[i][1]];
		for (int i = 1 ; i <= m ; ++i)
		{
			if (e[i][0] > e[i][1]) swap(e[i][0],e[i][1]);
			if (D[e[i][0]] > D[e[i][1]]) swap(e[i][0],e[i][1]);
			add(e[i][0],e[i][1],i);
		}
		for (int i = 1 ; i <= n ; ++i)
		{ 
			for (int j = st[i] ; j ; j = nx[j]) vis[to[j]][0] = i,vis[to[j]][1] = j;
			for (int j = st[i] ; j ; j = nx[j])
				for (int k = st[to[j]] ; k ; k = nx[k])
				if (vis[to[k]][0] == i) ++cnt[id[j]],++cnt[id[k]],++cnt[vis[to[k]][1]];
		}
		ll ans = 0;
		for (int i = 1 ; i <= m ; ++i) if (cnt[i] > 1) ans += (ll)(cnt[i] - 1) * cnt[i] / 2;
		printf("%lld\n",ans);
	}
	return 0;
}

你可能感兴趣的:(分块,思维,性质,图论)