原題連結
介紹一種(也許是正解的)卡常做法
先說總體思路:對於每個三元組 \((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\),十分優秀,成功卡過。