21.7.8 t3
阿新 • • 發佈:2021-07-08
tag:貪心
瞎猜一個結論,前 \(n\) 個一定是 \(1,2\cdots n\),可以用歸納法證明。
然後 \(n^2\) 很簡單,從 \(n\) 開始列舉匹配點,一對點不合法只有兩種情況:
- \(x,y\) 在同一個聯通塊內
- \(x,y\) 所在的聯通塊合併以後沒有可以連向外部的點
第二條的意思就是 \(cnt[bel[x]]=cnt[bel[y]]=1\)。
\(100pts\) 用 \(2n\) 個 set 維護一下每個聯通塊內可以用來匹配的點,再用 \(2\) 個 set 分別維護所有 \(cnt\ge1/cnt>1\) 的聯通塊的最小匹配點即可。
如果最小的那個和當前點處在同一聯通塊內,就取第二個。
合併兩個聯通塊時用啟發式合併,複雜度 \(O(nlog^2n)\)。
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } enum{ MAXN = 200005 }; int n, ans[MAXN]; int fa[MAXN], sz[MAXN]; int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);} set<int>::iterator it; set<int>p[MAXN], st1, st2; /* 1 all 2 sz>1 */ inline void merge(int u, int v){ /* i in u, ans[i] in v */ // printf("merge(%d %d)\n",u,v); if(!p[u].empty()){ st1.erase(*p[u].begin()); if(sz[u]>1) st2.erase(*p[u].begin()); } if(!p[v].empty()){ st1.erase(*p[v].begin()); if(sz[v]>1) st2.erase(*p[v].begin()); } p[v].erase(p[v].begin()); if(p[u].size() > p[v].size()) swap(p[u],p[v]); for(it=p[u].begin(); it!=p[u].end(); it++) p[v].insert(*it); p[u].clear(); fa[u] = v; sz[v] += sz[u]-2; if(!p[v].empty()){ st1.insert(*p[v].begin()); if(sz[v]>1) st2.insert(*p[v].begin()); } } inline void check(){ for(int i=1; i<=2*n; i++) if(Find(i)==i){ printf("%d sz=%d ",i,sz[i]); for(it=p[i].begin(); it!=p[i].end(); it++) printf("%d ",*it); puts(""); } for(it=st1.begin(); it!=st1.end(); it++) printf("%d ",*it); puts(""); for(it=st2.begin(); it!=st2.end(); it++) printf("%d ",*it); puts(""); } int main(){ // freopen("3.in","r",stdin); // freopen("333.out","w",stdout); Read(n); for(int i=1; i<=2*n; i++) fa[i] = i, sz[i] = 1; for(int i=n+1; i<=2*n; i++) p[i].insert(i); for(int i=1; i<n; i++){ int x, y; Read(x); Read(y); x = Find(x); y = Find(y); sz[y] += sz[x]; fa[x] = y; if(p[x].size() > p[y].size()) swap(p[x],p[y]); for(it=p[x].begin(); it!=p[x].end(); it++) p[y].insert(*it); p[x].clear(); } for(int i=1; i<=2*n; i++) if(Find(i)==i and !p[i].empty()){ st1.insert(*p[i].begin()); if(sz[i]>1) st2.insert(*p[i].begin()); } // check(); for(int i=n; i; i--){ // for(int j=n+1; j<=2*n; j++) if(vis[j]==false and Find(i)!=Find(j) and (i==1 or sz[Find(i)]+sz[Find(j)]>2)){ // ans[i] = j; // sz[Find(i)] += sz[Find(j)]-2; // fa[Find(j)] = Find(i); // break; // } // vis[ans[i]] = true; if(sz[Find(i)]==1 and i!=1){ assert(!st2.empty()); int cur = *st2.begin(); if(Find(cur)!=Find(i)) ans[i] = cur; else st2.erase(cur), assert(!st2.empty()), ans[i] = *st2.begin(), st2.insert(cur); } else{ assert(!st1.empty()); int cur = *st1.begin(); if(Find(cur)!=Find(i)) ans[i] = cur; else st1.erase(cur), assert(!st1.empty()), ans[i] = *st1.begin(), st1.insert(cur); } merge(Find(i),Find(ans[i])); // check(); } for(int i=1; i<=n; i++) printf("%d\n",i); for(int i=n; i; i--) printf("%d\n",ans[i]); return 0; }