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

Jerrycyx發表於2024-09-01

原題連結

介紹一種(也許是正解的)卡常做法

先說總體思路:對於每個三元組 \((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\)十分優秀,成功卡過。

相關文章