传送门
首先是 68 68 68分对于树的做法(和正解没有关系)
点编号为 1 − n 1-n 1−n
考虑对于每一个二进制位 d d d
把这编号位为1的点 m o d i f y modify modify
然后对每个点 q u e r y query query
如果颜色改变,那么 s u m + = 1 < < d sum+=1<
然后可以发现这样求出来每一个点相连点的编号异或和
然后考虑一个剥叶子的过程
对于每一个点
把 s u m [ u ] m o d i f y sum[u]\ modify sum[u] modify
如果 u u u颜色改变
那么 u u u之和 s u m [ u ] sum[u] sum[u]相连
于是就可以把这条边断开
继续对 s u m [ u ] sum[u] sum[u]操作
可以发现对于树一定可以把所有边找出来
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
不过有些特殊判断
( 代 码 n a m e s p a c e s o l v e 2 ) (代码namespace\ solve2) (代码namespace solve2)
考虑对于一个点 i i i
把 1 , n 1,n 1,n所有点除了 i i i都 m o d i f y modify modify
如果其颜色改变
那么一定连了奇数条边出去
然后对于 1 , m i d 1,mid 1,mid进行 m o d i f y modify modify
如果颜色也改变
那么必定有奇数条边连向 1 , m i d 1,mid 1,mid
于是继续做
对一个点是二分
于是可以对所有点整体二分
m o d i f y [ l , m i d ] , q u e r y ( m i d + 1 , r ) modify[l,mid],query(mid+1,r) modify[l,mid],query(mid+1,r)
然后把询问挂到 l , m i d l,mid l,mid上
题解说 可以证明随机情况下期望有 n / 3 n/3 n/3的点像前连了奇数条边
于是可以 r a n d rand rand一个排列这样做
做完之后把所有孤立点 c h e c k check check出除
复杂度 m l o g n mlogn mlogn
#include "explore.h"
#include
using namespace std;
#define cs const
#define re register
#define pii pair
#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();
}