【LOJ #3161】「NOI2019」I 君的探险(随机化+整体二分)
首先是分对于树的做法(和正解没有关系)
点编号为
考虑对于每一个二进制位
把这编号位为1的点
然后对每个点
如果颜色改变,那么
然后可以发现这样求出来每一个点相连点的编号异或和
然后考虑一个剥叶子的过程
对于每一个点
把
如果颜色改变
那么之和相连
于是就可以把这条边断开
继续对操作
可以发现对于树一定可以把所有边找出来
复杂度
不过有些特殊判断
考虑对于一个点
把所有点除了都
如果其颜色改变
那么一定连了奇数条边出去
然后对于进行
如果颜色也改变
那么必定有奇数条边连向
于是继续做
对一个点是二分
于是可以对所有点整体二分
然后把询问挂到上
题解说 可以证明随机情况下期望有的点像前连了奇数条边
于是可以一个排列这样做
做完之后把所有孤立点出除
复杂度
#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();
}