题解:P17209 「DLESS-6」XOR and Your Problem
MadSamurai · · 题解
提供一种根号分块和压位 Trie 以外的瞎搞做法。
记
长度为
固定
对于左端点
如果从右往左扫 out,若
还可以再砍一层。设扫到
即使
也可以把这样的
接下来用一棵反着写的 Fenwick 树保存它们。发现关键区间 chmax;查询左端点
void add(int x,int v){for(;x;x-=x&-x)bit[x]=max(bit[x],v);}
int ask(int x){int z=0;for(;x<=n;x+=x&-x)z=max(z,bit[x]);return z;}
扫到 ask(i) 正好等于 ask(l) 就变成
问题只剩不能真的对每个
写一个 work(l,r,out,x),其中 out 表示位置
有两种情况可以整段退出:
- 若
mx\le out ,这一段不可能出现新的后缀纪录点。 - 若
mx\le F(r,R-1) ,其中R 是当前扫描到的右端点,那么这一段也全部被旧答案盖住。因为左端点越往左,旧答案只会更大。
否则把区间从中间分开,先递归右半边,再把右半边得到的新 out 带到左半边。一定要先右后左,因为我们模拟的本来就是从右往左扫。父区间的最大值已经算过,右半边再查一次以后,若父区间最大值仍大于 out,它就只能来自左半边,因此左递归连最大值查询都能省掉一次。
区间很短时继续递归反而亏,所以长度不超过某个值就直接倒着扫。这里用了一个 CUT=64,换成附近的数一般也都能跑,只是常数有点区别。
最后需要支持固定 farthest 是
:::info[这个做法到底有多瞎]
正确性没有随机成分,work 跳过的区间要么不可能刷新后缀最大值,要么一定被旧答案覆盖。
若记所有 work 实际访问的分治结点数为
关键区间本身可以按异或 Trie 的层数摊到 CUT,所以我更愿意把它叫输出敏感瞎搞。预处理 Wavelet Matrix 为
:::
下面是实现:
#include<bits/stdc++.h>
using namespace std;
const int N=300005,K=30,CUT=64;
int sm[K][N],a[N],bit[N],head[N],ql[N],nxt[N],ans[N],n;
void add(int x,int v){for(;x;x-=x&-x)bit[x]=max(bit[x],v);}
int ask(int x){int z=0;for(;x<=n;x+=x&-x)z=max(z,bit[x]);return z;}
int farthest(int l,int r,int x){
int s=0,z=n,L=l-1,R=r,res=0;
for(int k=K-1;k>=0;k--){
int p0=sm[k][s+L]-sm[k][s],p1=sm[k][s+R]-sm[k][s];
int one=sm[k][s+z]-sm[k][s],zero=z-one,w=x>>k&1;
if(!w){
if(p0<p1){res|=1<<k;s+=zero;z=one;L=p0;R=p1;}
else z=zero,L-=p0,R-=p1;
}else{
int zl=L-p0,zr=R-p1;
if(zl<zr){res|=1<<k;z=zero;L=zl;R=zr;}
else s+=zero,z=one,L=p0,R=p1;
}
}
return res;
}
vector<pair<int,int> >up;
#ifdef LOCAL
long long calls,farq,ess;
#endif
int work(int l,int r,int out,int x,int w=-1,int h=-1){
if(l>r)return out;
#ifdef LOCAL
calls++;
#endif
if(w<0){w=farthest(l,r,x);
#ifdef LOCAL
farq++;
#endif
}
if(w<=out)return out;
if(h<0)h=ask(r);
if(w<=h)return w;
if(r-l+1<=CUT){
for(int i=r;i>=l;i--){
int z=a[i]^x;
if(z>out){if(z>ask(i))up.push_back({i,z});out=z;}
}
return out;
}
int m=(l+r)>>1;
int rw=farthest(m+1,r,x);
#ifdef LOCAL
farq++;
#endif
out=work(m+1,r,out,x,rw,h);
if(w<=out)return out;
return work(l,m,out,x,w);
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int q;cin>>n>>q;
vector<int>b(n),c(n);for(int i=1;i<=n;i++)cin>>a[i],b[i-1]=a[i];
vector<pair<int,int> >blk(1,{0,n}),nb;
for(int k=K-1;k>=0;k--){
for(int i=0;i<n;i++)sm[k][i+1]=sm[k][i]+(b[i]>>k&1);
nb.clear();
for(pair<int,int>z:blk){
int l=z.first,r=z.second,p=l;
for(int i=l;i<r;i++)if(!(b[i]>>k&1))c[p++]=b[i];
int m=p;
for(int i=l;i<r;i++)if(b[i]>>k&1)c[p++]=b[i];
if(l<m)nb.push_back({l,m});
if(m<r)nb.push_back({m,r});
}
b.swap(c);blk.swap(nb);
}
vector<int>().swap(b);vector<int>().swap(c);
vector<pair<int,int> >().swap(blk);vector<pair<int,int> >().swap(nb);
memset(head,-1,sizeof(int)*(n+1));
for(int i=0,r;i<q;i++)cin>>ql[i]>>r,nxt[i]=head[r],head[r]=i;
for(int r=1;r<=n;r++){
up.clear();if(r>1)work(1,r-1,-1,a[r]);
for(pair<int,int>z:up)add(z.first,z.second);
#ifdef LOCAL
ess+=up.size();
#endif
for(int i=head[r];i!=-1;i=nxt[i])ans[i]=ask(ql[i]);
}
for(int i=0;i<q;i++)cout<<ans[i]<<"\n";
#ifdef LOCAL
cerr<<"calls "<<calls<<" far "<<farq<<" essential "<<ess<<"\n";
#endif
}