loj3161「NOI2019」I 君的探险(随机化,整体二分)

loj3161「NOI2019」I 君的探险(随机化,整体二分)

loj

Luogu

题解时间

对于 $ N le 500 $ 的点,毫无疑问可以直接 $ O(n^2) $ 暴力询问解决。

考虑看起来最好做的 $ B $ 类。

由于有每个点的父亲编号小于该点的优良特性,很容易想到整体二分。

考虑用整体二分求出每个点的父亲:

对于一个分治区间,毫无疑问 $ [l,mid] $ 的节点的父亲在左区间。

而对于另外一半节点,考虑将左半节点全部modify,此时右半某个节点亮起则说明左半节点至少有一个与之相邻,则其父亲在左半区间内。

而对于其他数据,我们依然可以大胆尝试类似的做法,只不过要维护已经确定的边的影响,

并且注意每次将序列随机打乱,同时已经加完边的节点要从序列中删除。

#include<bits/stdc++.h>
// #include"explore.h"
using namespace std;
typedef long long lint;
struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}};
void modify(int x);
int query(int x);
void report(int x,int y);
int check(int x);
const int N=400011;
int n,m;
namespace task500
{
bool check(){return n<=500;}
int tag[N];
void explore()
{
	for(int i=0;i<n-1;i++){modify(i);for(int j=i+1;j<n;j++)if(query(j)!=tag[j]) tag[j]^=1,report(i,j);}
}
}
vector<int> ve[N];int tcnt;
namespace taskB
{
bool check(){return n==99997||n==199997;}
void fuck(int l,int r,int px)
{
	if(l==r){for(auto x:ve[px])if(x!=l) report(l,x);return;}
	int mid=l+r>>1,ls=++tcnt,rs=++tcnt;
	for(int i=l;i<=mid;i++) modify(i);
	for(auto x:ve[px])
		if(x<=mid||query(x)) ve[ls].push_back(x);
		else ve[rs].push_back(x);
	for(int i=l;i<=mid;i++) modify(i);
	fuck(l,mid,ls),fuck(mid+1,r,rs);
}
void explore()
{
	tcnt++;for(int i=0;i<n;i++) ve[tcnt].push_back(i);
	fuck(0,n-1,tcnt);
}
}
namespace tasknormal
{
int cnt;
int a[N],on[N];
vector<int> to[N];
void report(int x,int y){::report(x,y);to[x].push_back(y),to[y].push_back(x),cnt++;}
int query(int x){int ret=::query(x);for(auto y:to[x]) ret^=on[y];return ret;}
void fuck(int l,int r,int px)
{
	if(l==r){for(auto x:ve[px])if(x!=l) report(a[l],a[x]);ve[px].clear();return;}
	int mid=l+r>>1,ls=++tcnt,rs=++tcnt;
	for(int i=l;i<=mid;i++) modify(a[i]),on[a[i]]=1;
	for(auto x:ve[px])
		if(x<=mid||query(a[x])) ve[ls].push_back(x);
		else ve[rs].push_back(x);
	for(int i=l;i<=mid;i++) modify(a[i]),on[a[i]]=0;
	fuck(l,mid,ls),fuck(mid+1,r,rs);
	ve[px].clear();
}
void explore()
{
	for(int i=0;i<n;i++) a[i]=i;
	while(cnt<m)
	{
		random_shuffle(a,a+n);
		tcnt=1;for(int i=0;i<n;i++) ve[tcnt].push_back(i);
		fuck(0,n-1,tcnt);if(cnt<m)for(int i=0;i<n;i++)if(check(a[i])){swap(a[i],a[n-1]),n--,i--;}
	}
}
}
void explore(int n,int m)
{
	::n=n,::m=m;
	if(task500::check()) task500::explore();
	else if(taskB::check()) taskB::explore();
	else tasknormal::explore();
}
原文地址:https://www.cnblogs.com/rikurika/p/13298818.html