【LOJ #3161】「NOI2019」I 君的探险(随机化+整体二分)

传送门

首先是6868分对于树的做法(和正解没有关系)
点编号为1n1-n
考虑对于每一个二进制位dd
把这编号位为1的点modifymodify
然后对每个点queryquery
如果颜色改变,那么sum+=1<<dsum+=1<<d
然后可以发现这样求出来每一个点相连点的编号异或和

然后考虑一个剥叶子的过程
对于每一个点
sum[u] modifysum[u]\ modify
如果uu颜色改变
那么uu之和sum[u]sum[u]相连
于是就可以把这条边断开
继续对sum[u]sum[u]操作

可以发现对于树一定可以把所有边找出来
复杂度O(nlogn)O(nlogn)
不过有些特殊判断

(namespace solve2)(代码namespace\ solve2)


考虑对于一个点ii
1,n1,n所有点除了iimodifymodify
如果其颜色改变
那么一定连了奇数条边出去
然后对于1,mid1,mid进行modifymodify
如果颜色也改变
那么必定有奇数条边连向1,mid1,mid
于是继续做

对一个点是二分
于是可以对所有点整体二分
modify[l,mid],query(mid+1,r)modify[l,mid],query(mid+1,r)
然后把询问挂到l,midl,mid
题解说 可以证明随机情况下期望有n/3n/3的点像前连了奇数条边
于是可以randrand一个排列这样做
做完之后把所有孤立点checkcheck出除

复杂度mlognmlogn

#include "explore.h"
#include<bits/stdc++.h>
using namespace std;
#define cs const
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define ll long long
#define pb push_back
cs int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
inline int readstring(char *s){
	int top=0;
	char ch=gc();
	while(isspace(ch))ch=gc();
	while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
	return top;
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=300005;
int n,m;
namespace solve1{
	int sta[N];
	inline void main(){
		for(int i=0;i<n-1;i++){
			modify(i);
			for(int j=i+1;j<n;j++){
				int now=query(j);
				if(sta[j]!=now)report(i,j);
				sta[j]=now;
			}
		}
	}
}
namespace solve2{
	int sta[N],sum[N];
	queue<int> q;
	map<pii,bool>vt;
	inline void main(){
		for(int i=0;(1<<i)<=n;i++){
			for(int j=1;j<=n;j++)if(j&(1<<i)){
				modify(j-1);
			}
			for(int j=1;j<=n;j++){
				if(query(j-1)!=sta[j]){
					sum[j]+=1<<i,sta[j]^=1;
				}
			}
		}
		for(int i=1;i<=n;i++)sum[i]^=i;
		if(n%10>=7){
			for(int i=n;i;i--)if(sum[i]){
				report(i-1,sum[i]-1),sum[sum[i]]^=i;
			}
			return;
		}
		for(int i=1;i<=n;i++)q.push(i);
		while(!q.empty()&&m){
			int u=q.front();q.pop();
			if(sum[u]>0&&sum[u]<=n&&sum[u]!=u&&vt.find(pii(min(sum[u],u),max(sum[u],u)))==vt.end()){
				int pre=query(sum[u]-1);
				modify(u-1);
				if(query(sum[u]-1)!=pre){
					report(sum[u]-1,u-1),m--;
					q.push(sum[u]),vt[pii(min(sum[u],u),max(sum[u],u))]=1;
					sum[sum[u]]^=u,sum[u]=0;
				}
			}
		}
	}
}
namespace solve3{
	vector<int> q[N<<2];
	vector<int> e[N];
	int p[N],sta[N];
	#define lc (u<<1)
	#define rc ((u<<1)|1)
	#define mid ((l+r)>>1)
	void solve(int u,int l,int r){
		if(l==r){
			for(int &x:q[u]){
				report(x,p[l]);
				e[p[l]].pb(x),e[x].pb(p[l]);
			}
			return;
		}
		for(int i=l;i<=mid;i++){
			modify(p[i]);
			for(int &v:e[p[i]])sta[v]^=1;
		}
		for(int i=mid+1;i<=r;i++){
			if(query(p[i])!=sta[p[i]])
			q[lc].pb(p[i]);
		}
		for(int &x:q[u]){
			if(query(x)!=sta[x])
			q[lc].pb(x);
			else q[rc].pb(x);
		}
		for(int i=l;i<=mid;i++){
			modify(p[i]);
			for(int &v:e[p[i]])sta[v]^=1;
		}
		solve(lc,l,mid),solve(rc,mid+1,r);
	}
	#undef lc
	#undef rc
	#undef mid
	inline void main(){
		srand(0721);
		for(int i=0;i<n;i++)p[i+1]=i;
		while(n){
			random_shuffle(p+1,p+n+1);
			for(int i=1;i<=n;i++)sta[p[i]]=query(p[i]);
			solve(1,1,n);
			for(int i=1;i<=n*4;i++)q[i].clear();
			int nn=0;
			for(int i=1;i<=n;i++)if(!check(p[i]))p[++nn]=p[i];
			n=nn;
		}
	}
}
void explore(int _N, int _M) {
	n=_N,m=_M;
	if(n<=500)solve1::main();
	else if(n>m)solve2::main();
	else solve3::main();
}
posted @ 2019-12-18 21:34  Stargazer_cykoi  阅读(302)  评论(0编辑  收藏  举报