CF1559D Mocha and Diana 題解
阿新 • • 發佈:2021-08-20
Link.
Description.
給定兩棵森林,節點編號都是 \([1,n]\)。
每次操作選出兩個節點 \(x\) 和 \(y\),滿足在兩棵樹上 \(x\) 號節點均不和 \(y\) 號聯通,並把他們相連。
最大化操作次數,並構造。
Solution.
設第一片森林的連通塊個數為 \(tot_1\),第二片為 \(tot_2\)。
那我們答案上界是 \(n-\max(tot_1,tot_2)\)。
我們發現下界也是,嘗試證明。
考慮最終狀態,考慮連通塊數量多的一片森林,設這片為 \(A\),另一片為 \(B\)。
我們發現如果 \(A\) 中存在 \(x\)
所以我們說明了如果 \(A\) 中的不同連通塊內兩個數,他們在 \(B\) 中一定聯通。
所以如果 \(A\) 中連通塊數 \(\ge 2\),那我們發現 \(A\) 中兩兩聯通。
如果 \(A\) 中連通塊數 \(=1\),說明 \(B\) 中連通塊數 \(\le 1\),也兩兩聯通。
所以 D1 直接暴力列舉兩個點即可。
沒找到性質
然後考慮不暴力。
欽定一個觀察點 \(x\),先找到所有可以和它連邊的點,連掉。
如果兩邊都聯通,那對面點沒什麼花樣。
如果一邊聯通一邊不聯通,那找到所有這樣的兩組點(按哪邊聯通分類)。
然後儘量匹配即可。
因為 \(a\)
Coding.
點選檢視頹瘋程式碼
//是啊,你就是那隻鬼了,所以被你碰到以後,就輪到我變成鬼了{{{ #include<bits/stdc++.h> using namespace std;typedef long long ll; template<typename T>inline void read(T &x) { x=0;char c=getchar(),bz=0; for(;c<48||c>57;c=getchar()) if(!(c^45)) bz=1; for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48); bz?x=-x:x; }/*}}}*/ int n,m1,m2,a[100005],ta,b[100005],tb;vector<pair<int,int> >rs; struct dsu { int fa[100005]; inline void init(int n) {for(int i=1;i<=n;i++) fa[i]=i;} inline int getf(int x) {return fa[x]==x?x:fa[x]=getf(fa[x]);} inline void mrg(int x,int y) {x=getf(x),y=getf(y);if(x^y) fa[x]=y;} inline char chk(int x,int y) {return getf(x)!=getf(y);} }A,B; inline void ad(int x,int y) {rs.push_back(make_pair(x,y));} inline void flush() { printf("%d\n",(int)rs.size()); for(auto x:rs) printf("%d %d\n",x.first,x.second); } int main() { read(n),read(m1),read(m2),A.init(n),B.init(n); for(int i=1,x,y;i<=m1;i++) read(x),read(y),A.mrg(x,y); for(int i=1,x,y;i<=m2;i++) read(x),read(y),B.mrg(x,y); for(int i=2;i<=n;i++) if(A.chk(1,i)&&B.chk(1,i)) A.mrg(1,i),B.mrg(1,i),ad(1,i); for(int i=2;i<=n;i++) if(A.chk(1,i)) a[++ta]=i,A.mrg(1,i); for(int i=2;i<=n;i++) if(B.chk(1,i)) b[++tb]=i,B.mrg(1,i); for(int i=1;i<=ta&&i<=tb;i++) ad(a[i],b[i]); return flush(),0; }