NOI2019 I君的探险

link ### 题解 考虑B部分的做法。

通过分治找到每个点的父亲。

对于一个区间,考虑父亲在这个区间内的点,如果在左半边则父亲一定在左半边,否则,先modify一下左半边的每个点,如果这个点改变了则它的父亲在左半边,否则在右半边。接着对左右半边分治下去。

发现当一个点前面有奇数个点的时候,这样的操作一定可以找到恰好一个父亲

于是每次shuffle一次,做一次上述操作,删掉孤立点。

不过最前面的暴力部分和B要另写。

#include "explore.h"
#include<bits/stdc++.h>
using namespace std;
int a[200005],b[200005],ls[200005];
int t,fn;
vector<int> s[400005];
vector<int> to[200005];
void xreport(int x,int y){
	x=a[x];y=a[y];
	report(x,y);
	++fn;
	to[x].push_back(y);
	to[y].push_back(x);
}
int xquery(int x){
	x=a[x];
	int w=query(x);
	for(auto i:to[x])w^=b[i];
	return w;
}
void solve(int l,int r,int rt){
	if(l==r){
		int n=s[rt].size();
		for(int i=0;i<n;++i)if(s[rt][i]!=l)xreport(l,s[rt][i]);
	}
	else{
		int mid=(l+r)>>1;
		int x=t;
		for(int i=l;i<=mid;++i){modify(a[i]);b[a[i]]=1;}
		t+=2;
		int n=s[rt].size();
		for(int i=0;i<n;++i)if(s[rt][i]<=mid || xquery(s[rt][i])){ s[x+1].push_back(s[rt][i]);
		}
		else s[x+2].push_back(s[rt][i]);
		for(int i=l;i<=mid;++i){modify(a[i]);b[a[i]]=0;}
		solve(l,mid,x+1);	
		solve(mid+1,r,x+2);	
	}
	s[rt].clear();
}

void solveB(int l,int r,int rt){
	if(l==r){
		int n=s[rt].size();
		for(int i=0;i<n;++i)if(s[rt][i]!=l)report(l,s[rt][i]);
		return;
	}
	else{
		int mid=(l+r)>>1;
		int x=t;
		for(int i=l;i<=mid;++i)modify(i);
		t+=2;
		int n=s[rt].size();
		for(int i=0;i<n;++i)if(s[rt][i]<=mid || query(s[rt][i])){
			s[x+1].push_back(s[rt][i]);
		}
		else s[x+2].push_back(s[rt][i]);
		for(int i=l;i<=mid;++i)modify(i);
		solveB(l,mid,x+1);	
		solveB(mid+1,r,x+2);	
	}
}
void explore(int n, int m) {
	if(n%10==7){
		t=1;
		for(int i=0;i<n;++i)s[t].push_back(i);
		solveB(0,n-1,t);

		return ;
	}
	else if(n<=500){
		for(int i=0;i<n-1;++i){
			modify(i);
			for(int j=i+1;j<n;++j){
				if(query(j)!=ls[j]){
					ls[j]^=1;
					report(i,j);
				}
			}
		}
		return;
	}
	for(int i=0;i<n;++i){
		a[i]=i;
	}
	do{
		random_shuffle(a,a+n);	
		t=1;
		for(int i=0;i<n;++i)s[1].push_back(i);
		solve(0,n-1,1);
		if(fn<m){
			for(int i=0;i<n;++i)if(check(a[i])){swap(a[i],a[--n]);--i;}
		}
	}while(fn<m);
	t=1;
	for(int i=0;i<n;++i)s[t].push_back(i);
}