[CF1559D2]Mocha and Diana(hard version)
Posted Tan_tan_tann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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(uCF #738(div2)D2. Mocha and Diana (Hard Version)(贪心,并查集)
CF #738(div2)D1. Mocha and Diana (Easy Version)(暴力,并查集)
CF #738(div2)B. Mocha and Red and Blue(构造)
CF #738(div2)A. Mocha and Math(贪心)