「JOISC 2020 Day2」变色龙之恋 (交互题)(分治)(并查集)

传送门

  • O ( n 2 ) O(n^2) O(n2) 做法:两两查询一次,那么给颜色数为 1 的数对连边,那么一个点可以连出 1 或 3 条边
    对于一条的可以直接确定,对于 3 条的,假设当前位 i i i,它喜欢的为 x x x,喜欢它的为 y y y,颜色相同的为 z z z,那么当且仅当查询 ( i , y , z ) (i,y,z) (i,y,z) 时为 1,如果我们给 ( i , y ) , ( i , z ) (i,y),(i,z) (i,y),(i,z) 打上标记,那么两个颜色相同当且仅当这条边被两次打上标记
  • 利用二分图的性质,对于已知的边,可以把 [ 1 , i ) [1,i) [1,i) 的点分成两个集合,集合内均无边,然后在集合中分治,查询次数 3 log ⁡ n 3\log n 3logn,并且这个可以用并查集维护
    复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn),查询次数 O ( n ( 3 log ⁡ n + 6 ) ) O(n(3\log n+6)) O(n(3logn+6))
#include "chameleon.h"
//#include "grader.cpp"
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 1050;
int fa[N], c[N], id[N], d[N];
vector<int> G[N];
int fnd(int x){ return x==fa[x]?x:fnd(fa[x]); }
int fndc(int x){ return x==fa[x]?0:c[x]^fndc(fa[x]); }
void mrg(int u, int v){
	int fx=fnd(u), fy=fnd(v);
	if(d[fx]>d[fy]) swap(fx,fy), swap(v,u); 
	if(fx==fy) return;
	fa[fx]=fy; d[fy]=max(d[fy],d[fx]+1);
	c[fx]=fndc(u)^fndc(v)^1;
}
bool work_e(vector<int> S, int l, int r, int u, bool flg=0){
	if(G[u].size()==3) return false; 
	if(l>r) return false;
	if(!flg){
		vector<int> T; T.pb(u);
		for(int i=l; i<=r; i++) T.pb(S[i]);
		int c = Query(T); if(c>r-l+1) return false;	
	}
	if(l==r){ 
		G[u].pb(S[l]);
		G[S[l]].pb(u);
		mrg(u,S[l]); return true;
	}
	int mid = (l+r) >> 1;
	bool t = work_e(S,l,mid,u,false);
	work_e(S,mid+1,r,u,!t); 
	return true;
}
void Solve(int n){
	srand(time(0));
	for(int i=1; i<=n+n; i++) id[i]=i;
	random_shuffle(id+1,id+n+n+1);
	for(int i=1; i<=n+n; i++) fa[i]=i, c[i]=0;
	for(int i=1; i<=n+n; i++){
		vector<int> A, B;
		for(int j=1; j<i; j++) if(G[id[j]].size()!=3)
		fndc(id[j])?B.pb(id[j]):A.pb(id[j]);
		work_e(A,0,A.size()-1,id[i]);
		work_e(B,0,B.size()-1,id[i]);
	}
	static int mp[N][N], vis[N][N];
	for(int i=1; i<=n+n; i++) if(G[i].size()==1&&!vis[i][G[i][0]]) 
	vis[i][G[i][0]]=vis[G[i][0]][i]=1, Answer(i,G[i][0]);
	for(int i=1; i<=n+n; i++) if(G[i].size()>1){
		assert(G[i].size()==3);
		for(int u=0; u<3; u++) if(G[G[i][u]].size()==3)
		for(int v=u+1; v<3; v++) if(G[G[i][v]].size()==3){
			vector<int> S; S.pb(i), S.pb(G[i][u]), S.pb(G[i][v]);
			if(Query(S)==1){
				++mp[i][G[i][u]], ++mp[i][G[i][v]];
				++mp[G[i][u]][i], ++mp[G[i][v]][i];
				break;
			}
		}
	}
	for(int i=1; i<=n+n; i++)
	for(int j=i+1; j<=n+n; j++)
	if(mp[i][j]==2) Answer(i,j);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值