[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(贪心)

CF #738(div2)C. Mocha and Hiking(构造)

补题日记[2022牛客暑期多校1]D-Mocha and Railgun