介绍一种(也许是正解的)卡常做法
先说总体思路:对于每个三元组 \((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\),十分优秀,成功卡过。