题解:洛谷 P10996 【MX-J3-T3】Tuple

原题链接

介绍一种(也许是正解的)卡常做法

先说总体思路:对于每个三元组 \((x,y,z)\),若有一个 \(w\) 满足 \((x,y,w),(x,z,w),(y,z,w)\) 均存在,则找到了一个合法的四元组 \((x,y,z,w)\)

\(20\ \rm{Pts}\) 做法

如果暴力搜索,在遍历每一个三元组时,每一次都扫描所有的 \(w \in [1,N]\),判断是否存在三元组另需 \(O(M)\),时间复杂度是 \(O(NM^2)\),很明显会超时,应当勉强能得 \(20\) 分。我没打这个,就不贴代码了。

\(80\ \rm{Pts}\) 做法

考虑上述暴力做法的优化空间:

优化 #1

判断三元组是否存在可以不将所有三元组都扫描一遍,直接用一个桶 \(mp[x][y][z]\) 来记录,若存在则为 true。但是很明显,在 \(N \le 2000\) 时,三维数组会爆空间,所以我们就需要使用 unordered_map 作为哈希表来代替这个桶来记录三元组的存在(不用 map 是因为 unordered_map 时间复杂度更低)。

这样就可以 \(O(1)\) 查询三元组存在性,总时间复杂度降至 \(O(NM)\)

优化 #2

对于 \(w\) 的取值,它首先需要满足三元组 \((x,y,w)\) 存在,所以我们可以记录对于每一对 \((x,y)\),能与其组成三元组的所有 \(w\),在遍历三元组 \((x,y,z)\) 的时候只用判断能和 \((x,y)\) 组成三元组的 \(w\) 就可以了。

总时间复杂度进一步降为 \(O(M)\)

这样就可以拿 \(80\) 分了。

参考代码:

#include<cstdio>
#include<unordered_map>
#include<vector>
using namespace std;

const int N=2005,M=5e4+5;
int n,m,ans;
unordered_map<int,int> mp[N][N];
vector<int> v[N][N];

struct Peter{
	int x,y,z;
}p[M];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
		mp[p[i].x][p[i].y][p[i].z]=true;
		v[p[i].x][p[i].y].push_back(p[i].z);
	}
	for(int i=1;i<=m;i++)
	{
		int x=p[i].x,y=p[i].y,z=p[i].z;
		for(int w:v[x][y])
		{
			if(w==z) continue;
			if(mp[x][z][w]&&mp[y][z][w])
				ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

\(100\ \rm{Pts}\) 做法

上面之所以时间复杂度已经足够低,但依然无法满分,就是因为根据 unordered_map 的原理,如果试图寻找某一键值对应的数是,该键值不存在,那么它就会新建这个键值,增大占用空间。在极限数据下可能导致 MLE。

对应到上面的代码就是在执行 if(mp[x][z][w]&&mp[y][z][w]) 时,unordered_map 试图查询 mp[x][z][w]mp[y][z][w],但是如果这两个值不存在(以前没有被赋值或访问过),那么它就会在哈希表中加入 mp[x][z][w]mp[y][z][w]unordered_map 优化空间的原理就是没有用到值的就不开空间),造成了不必要的空间开销。

所以我们可以加上一个特判。因为 mp[x][y][z] 存在的前提是:

  • \(x,y\) 同时出现在一个三元组中过;
  • \(y,z\) 同时出现在一个三元组中过;
  • \(x,z\) 同时出现在一个三元组中过。

所以可以再开一个桶 \(tog\),令 \(tog_{x,y}\) 表示 \(x,y\) 在同一个三元组中出现过。

这样,在判断 if(mp[x][z][w] && mp[y][z][w]) 前,先判断 if(tog[x][w] && tog[z][w] && tog[y][w])。如果第二个条件都不满足,第一个条件自然也不可能满足,就可以省去不必要的对 mp[x][z][w]mp[y][z][w] 的访问,避免不必要的空间开销。

参考代码:

#include<cstdio>
#include<unordered_map>
#include<vector>
using namespace std;

const int N=2005,M=5e4+5;
int n,m,ans;
unordered_map<int,unordered_map<int,bool>> mp[N];
bool tog[N][N];
vector<int> v[N][N];

struct Peter{
	int x,y,z;
}p[M];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
		mp[p[i].x][p[i].y][p[i].z]=true;
		tog[p[i].x][p[i].y]=tog[p[i].x][p[i].z]=tog[p[i].y][p[i].z]=true;
		v[p[i].x][p[i].y].push_back(p[i].z);
	}
	for(int i=1;i<=m;i++)
	{
		int x=p[i].x,y=p[i].y,z=p[i].z;
		for(int w:v[x][y])
		{
			if(w==z) continue;
			if(tog[x][w]&&tog[z][w]&&tog[y][w])
				if(mp[x][z][w]&&mp[y][z][w]) ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

所有测试点中最大时间为 \(148ms\),最大空间为 \(182.84MB\)十分优秀,成功卡过。

posted @ 2024-09-01 08:24  Jerrycyx  阅读(12)  评论(0编辑  收藏  举报