[CF1559D2]Mocha and Diana(hard version)
Mocha and Diana
题解
我们可以将题目转化一下,我们的每个点在两个森林中肯定分别属于两个集合,设为
x
,
y
x,y
x,y,我们可以用一个点表示这个点所属的集合
(
x
,
y
)
(x,y)
(x,y)。
我们相当于每次要找到两个
x
,
y
x,y
x,y都不一样的点,将这两行与这两列合并。
就像普通的
K
r
u
s
k
a
l
Kruskal
Kruskal一样,容易发现,我们就直接暴力去寻找,将找到的能合并的点对都合并,我们所得到的答案一定是最优的,我们的答案与我们合并的点的顺序无关,转化到图上很好理解。
然而在CF的讨论区中
h
u
m
b
e
r
t
o
y
u
s
t
a
\color{orange}{humbertoyusta}
humbertoyusta 巨佬给出了一种
O
(
(
n
+
m
)
α
(
n
)
)
O\left((n+m)\alpha(n)\right)
O((n+m)α(n))的做法,就加在这里吧。
我们可以先选定其中一个点,让它先与所有与它既不在同一行,又不在同一列的点合并。
之后我们剩下的点肯定不能与之前选择的点合并了,它们肯定有一个地方在同一个集合,但我们可以将在同一行的点与在同一列的点合并,这两者合并后就与之前选择的点位置相同了。
我们可以一直执行这一个操作直到只有一列或一行有点。
这样我们的时间复杂度就只剩下并查集的
(
(
n
+
m
)
α
(
n
)
)
\left((n+m)\alpha(n)\right)
((n+m)α(n))了。
虽然实际上亲测只是我们下面的方法速度的
1
4
\frac{1}{4}
41。
考虑怎么维护这个过程。
我们可以以
x
x
x坐标为下标建线段树,而树上的节点维护这个区间内的
2
2
2个(如果有两个的话)
y
y
y坐标不同的点。
由于我们只需要找一个与它
x
,
y
x,y
x,y下标不同的点,所以我们只要在
x
x
x坐标不同的区域,看有没有一个
y
y
y坐标与它不同的点就行了,所以只需要维护
2
2
2个
y
y
y坐标不同的点。
而合并行与列的部分我们可以采用启发式合并。
我们用一个
s
e
t
set
set记录下来某一行与某一列有哪些点,将集合大小较小的行的所有点加到集合大小较大的行的集合中,列也同理。
每加一个点时就暴力将线段树上该点所在的
x
x
x坐标的状况更新一下。
交换行的时候其实是可以等加完这一行后在更改的,但交换列就得加一次更改一次了。或许只是因为我太菜了
由于是启发式合并,所以更改次数是
n
log
n
n\log\,n
nlogn的,每修改一次还要
O
(
log
n
)
O(\log\,n)
O(logn)地在线段树上修改一次。
由于我们输出的答案还得知道我们每次连接了哪些点,这就意味着我们还得同时维护每个点对
(
x
,
y
)
(x,y)
(x,y)对应的点。
当然,由于点对
(
x
,
y
)
(x,y)
(x,y)相同的点是等价的,我们可以直接用
m
a
p
map
map维护该点对对应的其中一个点。
实际上用
u
n
o
r
d
e
r
e
d
m
a
p
unordered_map
unorderedmap快不了多少,也优化不了复杂度。
时间复杂度 O ( n log 2 n ) O\left(n\log^2n\right) O(nlog2n)。
源码
O ( n log 2 n ) O\left(n\log^2n\right) O(nlog2n)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<unordered_map>
#include<set>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=1000000000000000000LL;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=100000;
const int n1=520;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m1,m2,fa[MAXN<<1],answer;
set<int>s[MAXN<<1];
unordered_map<int,int>mp[MAXN];
vector<pii>ans;
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u!=v)fa[u]=v;}
struct point{
int x,y,id;point(){x=y=id=0;}
point(int X,int Y,int Id){x=X;y=Y;id=Id;}
};
struct ming{
point a[2];
ming friend operator + (const ming &x,const ming &y){
ming res;if(!x.a[0].id)return y;if(!y.a[0].id)return x;
if(x.a[0].id&&x.a[1].id){res.a[0]=x.a[0];res.a[1]=x.a[1];return res;}
if(y.a[0].id&&y.a[1].id){res.a[0]=y.a[0];res.a[1]=y.a[1];return res;}
res.a[0]=x.a[0];if(x.a[0].y!=y.a[0].y)res.a[1]=y.a[0];return res;
}
};
class SegmentTree{
private:
ming tr[MAXN<<2];
public:
void modify(int rt,int l,int r,int ai){
if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;
if(l==r){
int cnt=0;tr[rt].a[0]=tr[rt].a[1]=point(0,0,0);
for(auto x:s[l]){tr[rt].a[cnt++]=point(l,x,mp[l][x]);if(cnt==2)break;}return ;
}
if(ai<=mid)modify(rt<<1,l,mid,ai);
if(ai>mid)modify(rt<<1|1,mid+1,r,ai);
tr[rt]=tr[rt<<1]+tr[rt<<1|1];
}
ming query(int rt,int l,int r,int al,int ar){
ming res;if(l>r||l>ar||r<al||al>ar)return res;
if(al<=l&&r<=ar)return tr[rt];int mid=l+r>>1;
if(al<=mid)res=res+query(rt<<1,l,mid,al,ar);
if(ar>mid)res=res+query(rt<<1|1,mid+1,r,al,ar);
return res;
}
}T;
signed main(){
read(n);read(m1);read(m2);makeSet(n+n);
for(int i=1,u,v;i<=m1;i++)read(u),read(v),unionSet(u,v);
for(int i=1,u,v;i<=m2;i++)read(u),read(v),unionSet(u+n,v+n);
for(int i=1,x,y;i<=n;i++)x=findSet(i),y=findSet(i+n),s[x].insert(y),s[y].insert(x),mp[x][y]=i;
for(int i=1;i<=n;i++)if(findSet(i)==i)T.modify(1,1,n,i);
for(int i=1;i<=n;i++)
while(1){
ming tmp=T.query(1,1,n,1,findSet(i)-1)+T.query(1,1,n,findSet(i)+1,n);
int k=findSet(i+n);if(!tmp.a[0].id||(tmp.a[0].y==k&&!tmp.a[1].id))break;
if(tmp.a[0].y==k)swap(tmp.a[0],tmp.a[1]);answer++;ans.push_back(mkpr(i,tmp.a[0].id));
k=findSet(i);int p=tmp.a[0].x,siz1=s[p].size(),siz2=s[k].size();
if(siz1<siz2){
for(auto x:s[p]){
s[k].insert(x),s[x].erase(p),s[x].insert(k);
if(!mp[k][x])mp[k][x]=mp[p][x];mp[p][x]=0;
}
s[p].clear();T.modify(1,1,n,p);T.modify(1,1,n,k);fa[p]=k;
}
else{
for(auto x:s[k]){
s[p].insert(x),s[x].erase(k),s[x].insert(p);
if(!mp[p][x])mp[p][x]=mp[k][x];mp[k][x]=0;
}
s[k].clear();T.modify(1,1,n,k);T.modify(1,1,n,p);fa[k]=p;
}
p=tmp.a[0].y;k=findSet(i+n);siz1=s[p].size();siz2=s[k].size();
if(siz1<siz2){
for(auto x:s[p]){
s[k].insert(x),s[x].erase(p),s[x].insert(k);
T.modify(1,1,n,x);if(!mp[x][k])mp[x][k]=mp[x][p];mp[x][p]=0;
}
s[p].clear();fa[p]=k;
}
else{
for(auto x:s[k]){
s[p].insert(x),s[x].erase(k),s[x].insert(p);
T.modify(1,1,n,x);if(!mp[x][p])mp[x][p]=mp[x][k];mp[x][k]=0;
}
s[k].clear();fa[k]=p;
}
}
printf("%d\n",answer);for(int i=0;i<answer;i++)printf("%d %d\n",ans[i].fir,ans[i].sec);
return 0;
}
O ( ( n + m ) α ( n ) ) O\left((n+m)\alpha(n)\right) O((n+m)α(n))
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<unordered_map>
#include<set>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=1000000000000000000LL;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=100000;
const int n1=520;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m1,m2,fa[MAXN<<1],answer;
vector<pii>ans;
queue<int>q1,q2;
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u!=v)fa[u]=v;}
signed main(){
read(n);read(m1);read(m2);makeSet(n+n);
for(int i=1,u,v;i<=m1;i++)read(u),read(v),unionSet(u,v);
for(int i=1,u,v;i<=m2;i++)read(u),read(v),unionSet(u+n,v+n);
for(int i=2;i<=n;i++)
if(findSet(i)!=findSet(1)&&findSet(i+n)!=findSet(1+n))
unionSet(1,i),unionSet(1+n,i+n),answer++,ans.pb(mkpr(1,i));
for(int i=2;i<=n;i++){
if(findSet(i)!=findSet(1))q1.push(i);
if(findSet(i+n)!=findSet(1+n))q2.push(i);
}
while(!q1.empty()&&!q2.empty()){
answer++;ans.pb(mkpr(q1.front(),q2.front()));
unionSet(q1.front(),q2.front());unionSet(q1.front()+n,q2.front()+n);
while(!q1.empty()&&findSet(q1.front())==findSet(1))q1.pop();
while(!q2.empty()&&findSet(q2.front()+n)==findSet(1+n))q2.pop();
}
printf("%d\n",answer);
for(int i=0;i<answer;i++)printf("%d %d\n",ans[i].fir,ans[i].sec);
return 0;
}