IOI2020 Day1 連線擎天樹 題解
阿新 • • 發佈:2020-09-19
安利:IOI2020題解-洛谷部落格 IOI2020題解-cnblogs
首先,如果存在 \(p_{i,j}=0\) ,那麼就分成了多個聯通塊,這些聯通塊分開做即可。
由於 $p_{i,j} \leq 3 $ 所以一個聯通塊當中不能出現多於一個環,否則必然會有兩個點之間有 \(4\) 條路徑。
沒有環的情況(聯通塊內 \(p_{i,j}\) 均為 \(1\) )先判掉,那麼這個聯通塊正好有一個環。
不難發現,如果兩個點 \(x\) 和 \(y\) 之間的路徑需要經過多於一個環上的節點,那麼 \(p_{x,y}\) 就等於 \(2\) ,否則 \(p_{x,y}\) 為 \(1\)
把所有的 \(p_{x,y} = 1\) 的 \((x,y)\) 合併到一個聯通塊中,然後把這些塊之間串成一個環,然後 \(check\) 是否合法即可。
\(\Theta (n^2)\)
code(LOJ上通過):
#include <bits/stdc++.h> #include "supertrees.h" using namespace std; const int N = 1001; int n,cur[N],cntc,G[N][N],ans[N][N]; int p[N],l,fa[N]; inline int Find(int x){ return x == fa[x] ? x : (fa[x] = Find(fa[x])); } inline void Merge(int x,int y){ fa[Find(x)] = Find(y); } inline void add_edge(int x,int y){ ans[x][y] = ans[y][x] = 1; } inline bool solve(int c){ int i,j; bool is = 0; for (l = 0,i = 1; i <= n; ++i) if (cur[i] == c) p[++l] = i; for (i = 1; i <= l; ++i) for (j = 1; j <= l; ++j) if (G[p[i]][p[j]] != 1) is = 1; if (l == 1) return 1; if (l == 2){ if (G[p[1]][p[2]] > 1) return 0; add_edge(p[1],p[2]); return 1; } if (!is){ for (i = 1; i < l; ++i) add_edge(p[i],p[i+1]); return 1; } for (i = 1; i <= l; ++i) fa[i] = i; for (i = 1; i <= l; ++i) for (j = 1; j <= l; ++j) if (G[p[i]][p[j]] == 1) Merge(i,j); for (i = 1; i <= l; ++i) fa[i] = Find(i); for (i = 1; i <= l; ++i) for (j = 1; j <= l; ++j) if (fa[i] == fa[j] && G[p[i]][p[j]] == 2) return 0; for (i = 1; i <= l; ++i) for (j = 1; j <= l; ++j) if (fa[i] != fa[j] && G[p[i]][p[j]] == 1) return 0; int x = 0,lst = 0,fir,cnt = 0; for (i = 1; i <= l; ++i){ x = is = 0; for (j = 1; j <= l; ++j) if (fa[j] == i) is = 1; if (!is) continue; ++cnt; for (j = 1; j <= l; ++j) if (j != i && fa[j] == i){ if (x) add_edge(p[x],p[j]); x = j; } if (x) add_edge(p[x],p[i]); if (lst) add_edge(p[lst],p[i]); else fir = i; lst = i; } if (cnt < 3) return 0; add_edge(p[lst],p[fir]); return 1; } inline void getcur(int x,int c){ cur[x] = c; for (int i = 1; i <= n; ++i) if (x != i && G[x][i] && !cur[i]) getcur(i,c); } inline int MAIN(){ int i,j; for (i = 1; i <= n; ++i) if (!cur[i]) getcur(i,++cntc); for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) if (cur[i] != cur[j] && G[i][j]) return 0; for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) if (cur[i] == cur[j] && !G[i][j]) return 0; for (i = 1; i <= cntc; ++i) if (!solve(i)) return 0; vector<vector<int> >Ans; vector<int>ret; Ans.clear(); for (i = 1; i <= n; ++i){ ret.resize(n); for (j = 0; j < n; ++j) ret[j] = ans[i][j+1],cerr << ans[i][j+1] << ' '; cerr<<'\n'; Ans.push_back(ret); } build(Ans); return 1; } int construct(vector<vector<int> > p){ int i,j; n = p[0].size(); for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) if (p[i][j] == 3) return 0; for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) G[i][j] = p[i-1][j-1]; return MAIN(); } /* int main(){ int n,i,j; cin >> n; vector<vector<int> >p; vector<int>q; p.clear(),q.resize(n); for (i = 1; i <= n; ++i){ for (j = 0; j < n; ++j) cin >> q[j]; p.push_back(q); } cout << construct(p) << '\n'; } */