[NOI2019]I 君的探险

大概思路如下:

我们随便点亮一个点集S,假设此时T集合中的点是亮的,就代表着T集合中的每个点到S集合至少有一条边,我们通过整体二分可以找出这条边。可以证明这样找到一条边的期望次数是log的,因此总次数为O(mlogn),加些随机化就过了。

#include<cstdio>
#define maxn 1000000
#define mid (l+r>>1)
void modify(int x);
int query(int x);
void report(int x, int y);
int check(int x);
int s[maxn],a[maxn],z1[maxn],z2[maxn],ans[maxn],v[maxn];
void solve(int l,int r,int L,int R){
	if(l==r){
		for(int i=L;i<=R;i++) ans[a[i]]=l;
		return;
	}
	for(int i=l;i<=mid;i++) modify(i);
	int t1=0,t2=0,tt=L-1;
	for(int i=L;i<=R;i++){
		int fl=0;
		if(query(a[i])^s[a[i]]) s[a[i]]^=1,fl=1;
		if(fl||(a[i]<=mid)) z1[++t1]=a[i];
		else z2[++t2]=a[i];
	}
	for(int i=1;i<=t1;i++) a[++tt]=z1[i];for(int i=1;i<=t2;i++) a[++tt]=z2[i]; 
	solve(l,mid,L,L+t1-1); solve(mid+1,r,L+t1,R);
}
void explore(int N, int M) {
	if(N<=500){
		for(int i=0;i<N-1;i++){
			modify(i);
			for(int j=i+1;j<N;j++){
				if(query(j)^s[j]) report(i,j),s[j]^=1;
			}
		}
	}else if(N%10==8){
		for(int i=0,l=1;l<=N;i++,l<<=1){
			for(int j=0;j<N;j++){
				int x=((j>>i)&1),y=i?((j>>(i-1))&1):0;
				if(x^y) modify(j);
			}
			for(int j=0;j<N;j++){
				if(query(j)) v[j]|=1<<i;
			}
		}
		for(int i=0;i<N;i++) if(i<(v[i]^i)) report(i,v[i]^i);
	}else if(N%10==7){
		for(int i=0;i<N;i++)a[i]=i;
		solve(0,N-1,1,N-1);
		for(int i=1;i<N;i++)report(ans[i],i);
	}else if(N%10==6){
		for(int i=0,l=1;l<=N;i++,l<<=1){
			for(int j=0;j<N;j++){
				int x=((j>>i)&1),y=i?((j>>(i-1))&1):0;
				if(x^y) modify(j);
			}
			for(int j=0;j<N;j++){
				if(query(j)) v[j]|=1<<i,s[j]=1;
				else s[j]=0;
			}
		}
		modify(0);int t1=0;
		for(int i=1;i<N;i++)if(query(i)^s[i]) z1[++t1]=i;
		for(int i=1;i<=t1;i++){
			for(int now=z1[i],lst=0,x;now;x=lst,lst=now,now=v[now]^now^x)
			report(now,lst);
		}
	}
}

  ps:56分

posted on 2020-07-08 22:55  啊啊啊起个什么名字好  阅读(181)  评论(0编辑  收藏  举报

导航