找三元环(bzoj 3498: PA2009 Cakes)

问题:给你一个无向图,问其中有多少个三元环

方法:暴力

复杂度:O(msqrt(m))

步骤:按点的度数分成两类,分别暴力

①统计每个点的度数

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

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

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

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

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

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


#include<stdio.h>
#include<string.h>
#include<math.h>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> G[100005], low, up;
map<int, int> p[100005];
int vis[100005], in[100005];
int Read()
{
	int x = 0, f = 1;
	char ch;
	ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')  f = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
		x = x*10+ch-'0', ch = getchar();
	return x*f;
}
int main(void)
{
	int n, m, i, j, x, y, z, k, ans;
	scanf("%d%d", &n, &m);
	for(i=1;i<=m;i++)
	{
		x = Read(), y = Read();
		G[x].push_back(y);
		G[y].push_back(x);
		p[x][y] = 1;
		p[y][x] = 1;
		in[x]++, in[y]++;
	}
	for(i=1;i<=n;i++)
	{
		if(in[i]<=sqrt(m))
			low.push_back(i);
		else
			up.push_back(i);
	}
	ans = 0;
	for(i=0;i<low.size();i++)
	{
		x = low[i];
		vis[x] = 1;
		for(j=0;j<G[x].size();j++)
		{
			y = G[x][j];
			if(vis[y])
				continue;
			for(k=j+1;k<G[x].size();k++)
			{
				z = G[x][k];
				if(vis[z]==0 && p[y].count(z))
					ans++;
			}
		}
	}
	for(i=0;i<up.size();i++)
	{
		for(j=i+1;j<up.size();j++)
		{
			x = up[i], y = up[j];
			if(p[x].count(y)==0)
				continue;
			for(k=j+1;k<up.size();k++)
			{
				z = up[k];
				if(p[y].count(z) && p[x].count(z))
					ans++;
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}

当然这个判断方法常数有点大

TLE的话可以看这个:http://blog.csdn.net/jaihk662/article/details/77750832


3498: PA2009 Cakes

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 470   Solved: 135
[ Submit][ Status][ Discuss]

Description

N个点m条边,每个点有一个点权a。
对于任意一个三元环(j,j,k)(i<j<k),它的贡献
为max(ai,aj,ak) 
求所有三元环的贡献和。
N<100000,,m<250000。

Input

The first line of the standard input contains two integers  n and m (1<=N<=100000,1<=M<=250000) separated by a single space and denoting the number of confectioners at the convention and the number of pairs of them that like each other. The participants of the convention are numbered from  1 to N, The second line contains n integers pi (1<=Pi<=1000000) separated by single spaces and denoting the requirements of respective confectioners for flour (in decagrams). The following m lines contain data about pairs of contestants that like each other. Each of these lines contains two integers ai and bi (1<=ai,bi<=n Ai<>Bi) separated by a single space. They denote that confectioners ai and bi like each other. We assume that all pairs of participants of the convention apart from the ones listed in the input would not like to bake cakes together. Each pair of confectioners appears at most once in the input. 

Output

The first and only line of the standard output should contain a single integer - the quantity of flour that will be used by all teams in total, in decagrams. 

Sample Input

5 7
1 5 3 4 2
1 2
2 3
5 2
4 3
3 1
1 4
5 1

Sample Output

14


这道题可以用上面的方法直接暴力出答案,但是会超时

可以考虑建有向边,步骤如下:

①所有点按权值从大到小排序(rank[]即排名)

②如果a和b有条边,rank[a]<rank[b],那么建条从a到b的边,否则建从b到a的边,中间记录每个点的出度

③按排名遍历,因为是有向边,所以不会重复

④对于当前点a,遍历与a点相邻的所有点bi,如果bi的出度<sqrt(m),那么再遍历所有与bi点相邻的点看是否也与a点相邻即可,否则就暴力与a点相邻的任意两点是否也相邻


#include<stdio.h>
#include<vector>
#include<map>
#include<math.h>
#include<algorithm>
using namespace std;
typedef struct Res
{
	int val;
	int id;
	bool operator < (const Res &b) const
	{
		if(val>b.val)
			return 1;
		return 0;
	}
}Res;
Res s[100005];
vector<int> G[100005];
map<int, int> p[100005];
int rak[100005], out[100005], link[100005];
int main(void)
{
	long long ans;
	int n, i, j, m, k, x, y, z;
	scanf("%d%d", &n, &m);
	for(i=1;i<=n;i++)
	{
		scanf("%d", &s[i].val);
		s[i].id = i;
	}
	sort(s+1, s+n+1);
	for(i=1;i<=n;i++)
		rak[s[i].id] = i;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d", &x, &y);
		if(rak[x]<rak[y])
			G[x].push_back(y), out[x]++, p[x][y] = 1;
		else
			G[y].push_back(x), out[y]++, p[y][x] = 1;
	}
	ans = 0;
	for(i=1;i<=n;i++)
	{
		x = s[i].id;
		for(j=0;j<G[x].size();j++)
			link[G[x][j]] = x;
		for(j=0;j<G[x].size();j++)
		{
			y = G[x][j];
			if(out[y]>sqrt(m)+1)
			{
				for(k=0;k<G[x].size();k++)
				{
					z = G[x][k];
					if(p[y].count(z))
						ans += s[i].val;
				}
			}
			else
			{
				for(k=0;k<G[y].size();k++)
				{
					z = G[y][k];
					if(link[z]==x)
						ans += s[i].val;
				}
			}
		}
	}
	printf("%lld\n", ans);
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值