1. 程式人生 > 其它 >CF1559D Mocha and Diana 題解

CF1559D Mocha and Diana 題解

Codeforces
Luogu

Description.

給定兩棵森林,節點編號都是 \([1,n]\)
每次操作選出兩個節點 \(x\)\(y\),滿足在兩棵樹上 \(x\) 號節點均不和 \(y\) 號聯通,並把他們相連。
最大化操作次數,並構造。

Solution.

設第一片森林的連通塊個數為 \(tot_1\),第二片為 \(tot_2\)
那我們答案上界是 \(n-\max(tot_1,tot_2)\)
我們發現下界也是,嘗試證明。
考慮最終狀態,考慮連通塊數量多的一片森林,設這片為 \(A\),另一片為 \(B\)
我們發現如果 \(A\) 中存在 \(x\)

\(y\) 不聯通,而 \(B\) 中它們也不聯通,那就可以對 \(x\)\(y\) 操作。
所以我們說明了如果 \(A\) 中的不同連通塊內兩個數,他們在 \(B\) 中一定聯通。
所以如果 \(A\) 中連通塊數 \(\ge 2\),那我們發現 \(A\) 中兩兩聯通。
如果 \(A\) 中連通塊數 \(=1\),說明 \(B\) 中連通塊數 \(\le 1\),也兩兩聯通。
所以 D1 直接暴力列舉兩個點即可。


沒找到性質
然後考慮不暴力。
欽定一個觀察點 \(x\),先找到所有可以和它連邊的點,連掉。
如果兩邊都聯通,那對面點沒什麼花樣。
如果一邊聯通一邊不聯通,那找到所有這樣的兩組點(按哪邊聯通分類)。
然後儘量匹配即可。
因為 \(a\)

\(x\) 聯通,\(x\)\(b\) 不聯通可以推出 \(a\)\(b\) 不聯通。

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;
}