P3380 【模板】二逼平衡树(树套树)题解
前言
前置知识:分块,二分。
虽然本题是一个树套树,但在这里给出一个思维难度较低的分块算法。
思路
首先,看到这种排名或者前驱后继的问题我们第一眼想到权值线段树,但这是区间排名,需要可持久化再套一颗其他的树,为了避免思路太过复杂,我们考虑分块。
假设我们将整个序列分成
-
对于区间查询排名,散块直接暴力查找有多少小于它的,对于整块,在排好序的序列里,我们直接二分找到第一个大于等于它的位置,然后所有在这个位置之前的数都小于它,再将所有小于它的所有位置求一个和即可。
-
求区间第
小,我们可以先进行二分答案,在 时,我们可以先根据上面的方法找到小于这个数值的数有多少个。然后,如果这个数值 ,则说明这个数可行,并且大了,则 ,否则 。特别注意,我们找到的是最小的大于 个数的值,而并非第 小,所有在算答案时,要减一。 -
单点修改。先在原数组上修改,然后直接放入排好序的数组中重新排序即可。
-
区间
的前驱。对于散块,找到最大的小于 的值。对于整块,通过在排好序的数组里二分查找,找到最大的小于 的值,然后对于所有算出来的值取一个 即可。 -
区间
的后继。对于散块,找到最小的大于 的值。对于整块,通过在排好序的数组里二分查找,找到最小的大于 的值,然后对于所有算出来的值取一个 即可。
可以发现,时间复杂度最高的是区间第
代码
#include<bits/stdc++.h>
using namespace std;
#define re register
int n,m;
int a[100005];
int add[1605],tot[100005],x[1605],y[1605];
int val[1605][1605];
inline char gc(){static char buf[1000010],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000010,stdin),p1==p2)?EOF:*p1++;}
template<typename T>
inline void fast_read(re T&x){x=0;re bool f=0;static char s=gc();while(s<'0'||s>'9')f|=s=='-',s=gc();while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+(s^48),s=gc();if(f)x=-x;}
static char buf[1000005];int len=-1;
inline void flush(){fwrite(buf,1,len+1,stdout);len=-1;}
inline void __PC(const char x){if(len==1000000)flush();buf[++len]=x;}
template<typename T>
inline void fast_write(re T x){if(x<0)x=-x,__PC('-');if(x>9)fast_write(x/10);__PC(x%10^48);}
#define fr fast_read
#define fw fast_write
#define fs flush
void init()
{
int len=n==1?1:sqrt(n*log2(n)),num=n/len;
// cout<<len<<endl;
for(int i=1;i<=num;++i)
{
x[i]=(i-1)*len+1,y[i]=i*len;
for(int j=x[i];j<=y[i];++j)
{
tot[j]=i;
val[i][j-x[i]+1]=a[j];
}
sort(val[i]+1,val[i]+len+1);
}
if(len*num<n)
{
x[num+1]=len*num+1,y[num+1]=n;
for(int i=len*num+1;i<=n;++i)
{
tot[i]=num+1;
val[num+1][i-len*num]=a[i];
}
sort(val[num+1]+1,val[num+1]+y[num+1]-x[num+1]+2);
}
}
int now[1605],nxt[1605];
int query(int l,int r,int k)
{
if(r-l+1<k) return -1;
int p=tot[l],q=tot[r];
if(p==q)
{
for(int i=l;i<=r;++i) now[i-l+1]=a[i];
sort(now+1,now+r-l+2);
return now[k]+add[p];
}
else
{
int ll=0,rr=0,mid,ans;
for(int i=l;i<=y[p];++i) ll=min(a[i]+add[p],ll),rr=max(a[i]+add[p],rr),now[i-l+1]=a[i]+add[p];
for(int i=x[q];i<=r;++i) rr=max(a[i]+add[q],rr),ll=min(a[i]+add[q],ll),nxt[i-x[q]+1]=a[i]+add[q];
for(int i=p+1;i<q;++i)
{
ll=min(val[i][y[i]-x[i]+1]+add[i],ll);
rr=max(val[i][y[i]-x[i]+1]+add[i],rr);
}
--ll,++rr;
sort(now+1,now+y[p]-l+2);
sort(nxt+1,nxt+r-x[q]+2);
while(ll<=rr)
{
mid=(ll+rr)>>1;
int noww=0;
int qwq=lower_bound(now+1,now+y[p]-l+2,mid)-now;
noww+=qwq-1;
qwq=lower_bound(nxt+1,nxt+r-x[q]+2,mid)-nxt;
noww+=qwq-1;
for(int i=p+1;i<q;++i)
{
qwq=lower_bound(val[i]+1,val[i]+y[i]-x[i]+2,mid-add[i])-val[i];
noww+=qwq-1;
}
if(noww>=k) rr=mid-1,ans=mid;
else ll=mid+1;
}
return ans-1;
}
}
void change(int l,int k)
{
int p=tot[l];
a[l]=k;
for(int i=x[p];i<=y[p];++i) val[p][i-x[p]+1]=a[i];
sort(val[p]+1,val[p]+y[p]-x[p]+2);
}
int query2(int l,int r,int k)
{
int p=tot[l],q=tot[r];
int res=0;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]<k) ++res;
return res+1;
}
else
{
for(int i=l;i<=y[p];++i) res+=a[i]<k;
for(int i=x[q];i<=r;++i) res+=a[i]<k;
for(int i=p+1;i<q;++i) res+=lower_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i]-1;
return res+1;
}
}
int front(int l,int r,int k)
{
int p=tot[l],q=tot[r];
int ans=-2147483647;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]<k) ans=max(ans,a[i]);
}
else
{
for(int i=l;i<=y[p];++i) if(a[i]<k) ans=max(ans,a[i]);
for(int i=x[q];i<=r;++i) if(a[i]<k) ans=max(ans,a[i]);
for(int i=p+1;i<q;++i)
{
int p=lower_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i]-1;
if(p==0) continue;
ans=max(ans,val[i][p]);
}
}
return ans;
}
int back(int l,int r,int k)
{
int p=tot[l],q=tot[r];
int ans=2147483647;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]>k) ans=min(ans,a[i]);
}
else
{
for(int i=l;i<=y[p];++i) if(a[i]>k) ans=min(ans,a[i]);
for(int i=x[q];i<=r;++i) if(a[i]>k) ans=min(ans,a[i]);
for(int i=p+1;i<q;++i)
{
int p=upper_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i];
if(p==y[i]-x[i]+2) continue;
ans=min(ans,val[i][p]);
}
}
return ans;
}
int main()
{
fr(n),fr(m);
for(int i=1;i<=n;++i)
{
fr(a[i]);
}
init();
while(m--)
{
int op,l,r,k;
fr(op);
if(op==1)
{
fr(l),fr(r),fr(k);
fw(query2(l,r,k));
__PC('\n');
}
else if(op==2)
{
fr(l),fr(r),fr(k);
fw(query(l,r,k));
__PC('\n');
}
else if(op==3)
{
fr(l),fr(k);
change(l,k);
}
else if(op==4)
{
fr(l),fr(r),fr(k);
fw(front(l,r,k));
__PC('\n');
}
else
{
fr(l),fr(r),fr(k);
fw(back(l,r,k));
__PC('\n');
}
}
fs();
return 0;
}
哎等等,先别走啊,你都不看看这份代码能得多少分吗。你发现,你得到了
卡常历程
首先,我发现第二个点和倒数第二个点
然后,我考虑常数优化,加上了一系列的
这时,我猛然发现,这题并没有强制在线,并且他是直接单点赋值,而非增加,所以我考虑将所有东西输入完后离散化,这样二分的范围就可以控制在
然后我又测了一下倒数第
接着,我又着手于常数优化,在操作
然后我想如果这倒数第二个点专门用来卡分块,那么他的修改操作肯定较少,所以我考虑把我算过的答案记录下来,在查询时如果记录过这个查询,则直接输出。这种玄学优化,又使得我来到了
在各种卡常无果的情况下,我将整块的手动二分优化,带到了散块上面,这一下直接让我卡进了
我顿时看到了曙光。我先将数组压着开,然后将
在万般困顿的最后,我又想起了老方法,条块长。我先将
我长舒了一口气,算是没有功亏一篑。
最终代码:
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<map>
using namespace std;
#define re register
// #define sort stable_sort*/
int n,m;
int a[50005];
int tot[50005],x[75],y[75];
int val[75][755];
int ls[100005],lslen;
inline char gc(){static char buf[100010],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,100010,stdin),p1==p2)?EOF:*p1++;}
template<typename T>
inline void fast_read(re T&x){x=0;re bool f=0;static char s=gc();while(s<'0'||s>'9')f|=s=='-',s=gc();while(s>='0'&&s<='9')x=(x<<3)+(x<<1)+(s^48),s=gc();if(f)x=-x;}
static char buf[100005];int len=-1;
inline void flush(){fwrite(buf,1,len+1,stdout);len=-1;}
inline void __PC(const char x){if(len==100000)flush();buf[++len]=x;}
template<typename T>
inline void fast_write(re T x){if(x<0)x=-x,__PC('-');if(x>9)fast_write(x/10);__PC(x%10^48);}
#define fr fast_read
#define fw fast_write
#define fs flush
bool flg;
inline void init()
{
re int len=n==1?1:sqrt(n)+491,num=n/len;
// cout<<len<<endl;
for(int i=1;i<=num;++i)
{
x[i]=(i-1)*len+1,y[i]=i*len;
for(int j=x[i];j<=y[i];++j)
{
tot[j]=i;
val[i][j-x[i]+1]=a[j];
}
sort(val[i]+1,val[i]+len+1);
}
if(len*num<n)
{
x[num+1]=len*num+1,y[num+1]=n;
for(int i=len*num+1;i<=n;++i)
{
tot[i]=num+1;
val[num+1][i-len*num]=a[i];
}
sort(val[num+1]+1,val[num+1]+y[num+1]-x[num+1]+2);
}
}
int now[755],nxt[755];
map<pair<int,pair<int,int> >,int> uni;
inline int query(re const int l,re const int r,re const int k)
{
const int p=tot[l],q=tot[r];
if(p==q)
{
#pragma unroll(20)
for(int i=l;i<=r;++i) now[i-l+1]=a[i];
sort(now+1,now+r-l+2);
return ls[now[k]];
}
else
{
re int ll=1,rr=lslen+1,mid,ans;
#pragma unroll(20)
for(re int i=l;i<=y[p];++i) now[i-l+1]=a[i];
#pragma unroll(20)
for(re int i=x[q];i<=r;++i) nxt[i-x[q]+1]=a[i];
// --ll,++rr;
sort(now+1,now+y[p]-l+2);
sort(nxt+1,nxt+r-x[q]+2);
while(ll<=rr)
{
re int maxx=0;
mid=(ll+rr)>>1;
re int noww=0;
re int midd=(y[p]-l+2)>>1;
re int qwq;
if(now[midd]<mid) qwq=lower_bound(now+midd+1,now+y[p]-l+2,mid)-now-1;
else qwq=lower_bound(now+1,now+midd,mid)-now-1;
maxx=max(maxx,now[qwq]);noww+=qwq;
midd=(r-x[q]+2)>>1;
if(nxt[midd]<mid) qwq=lower_bound(nxt+midd+1,nxt+r-x[q]+2,mid)-nxt-1;
else qwq=lower_bound(nxt+1,nxt+midd,mid)-nxt-1;
maxx=max(maxx,nxt[qwq]);noww+=qwq;
#pragma unroll(20)
for(re int i=p+1;i<q;++i)
{
midd=(y[i]-x[i]+2)>>1;
if(val[i][midd]<mid)
qwq=lower_bound(val[i]+1+midd,val[i]+y[i]-x[i]+2,mid)-val[i]-1;
else
qwq=lower_bound(val[i]+1,val[i]+midd,mid)-val[i]-1;
maxx=max(maxx,val[i][qwq]);
noww+=qwq;
}
if(noww>=k)
{
rr=maxx,ans=maxx;
if(noww==k) break;
}
else ll=mid+1;
}
return ls[ans];
}
}
inline void change(const int l,const int k)
{
const int p=tot[l];
a[l]=k;
for(int i=x[p];i<=y[p];++i) val[p][i-x[p]+1]=a[i];
sort(val[p]+1,val[p]+y[p]-x[p]+2);
}
inline int query2(const int l,const int r,const int k)
{
const int p=tot[l],q=tot[r];
re int res=0;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]<k) ++res;
return res+1;
}
else
{
for(int i=l;i<=y[p];++i) res+=a[i]<k;
for(int i=x[q];i<=r;++i) res+=a[i]<k;
for(int i=p+1;i<q;++i) res+=lower_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i]-1;
return res+1;
}
}
inline int front(const int l,const int r,const int k)
{
re const int p=tot[l],q=tot[r];
re int ans=-2147483647;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]<k) ans=max(ans,a[i]);
}
else
{
for(int i=l;i<=y[p];++i) if(a[i]<k) ans=max(ans,a[i]);
for(int i=x[q];i<=r;++i) if(a[i]<k) ans=max(ans,a[i]);
for(int i=p+1;i<q;++i)
{
int p=lower_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i]-1;
if(p==0) continue;
ans=max(ans,val[i][p]);
}
}
if(ans>0)
return ls[ans];
else
return -2147483647;
}
inline int back(const int l,const int r,const int k)
{
const int p=tot[l],q=tot[r];
re int ans=2147483647;
if(p==q)
{
for(int i=l;i<=r;++i) if(a[i]>k) ans=min(ans,a[i]);
}
else
{
for(re int i=l;i<=y[p];++i) if(a[i]>k) ans=min(ans,a[i]);
for(re int i=x[q];i<=r;++i) if(a[i]>k) ans=min(ans,a[i]);
for(int i=p+1;i<q;++i)
{
int p=upper_bound(val[i]+1,val[i]+y[i]-x[i]+2,k)-val[i];
if(p==y[i]-x[i]+2) continue;
ans=min(ans,val[i][p]);
}
}
if(ans!=2147483647)
return ls[ans];
else
return 2147483647;
}
int op[50005],l[50005],r[50005],k[50005];
int main()
{
fr(n),fr(m);
for(re int i=1;i<=n;++i)
{
fr(a[i]);
ls[++lslen]=a[i];
}
for(re int i=1;i<=m;++i)
{
fr(op[i]);
if(!(op[i]^1))
{
fr(l[i]),fr(r[i]),fr(k[i]);
ls[++lslen]=k[i];
}
else if(!(op[i]^2))
{
fr(l[i]),fr(r[i]),fr(k[i]);
}
else if(!(op[i]^3))
{
fr(l[i]),fr(k[i]);
ls[++lslen]=k[i];
}
else if(!(op[i]^4))
{
fr(l[i]),fr(r[i]),fr(k[i]);
ls[++lslen]=k[i];
}
else
{
fr(l[i]),fr(r[i]),fr(k[i]);
ls[++lslen]=k[i];
}
}
sort(ls+1,ls+lslen+1);
lslen=unique(ls+1,ls+lslen+1)-ls-1;
for(re int i=1;i<=n;++i) a[i]=lower_bound(ls+1,ls+lslen+1,a[i])-ls;
init();
for(re int i=1;i<=m;++i)
{
if(op[i]^2)
k[i]=lower_bound(ls+1,ls+lslen+1,k[i])-ls;
if(!(op[i]^1))
{
fw(query2(l[i],r[i],k[i]));
__PC('\n');
}
else if(!(op[i]^2))
{
if(uni.count(make_pair(l[i],make_pair(r[i],k[i])))&&!flg) fw(uni[make_pair(l[i],make_pair(r[i],k[i]))]),__PC('\n');
else
{
re int qwq=query(l[i],r[i],k[i]);
if(!flg) uni[make_pair(l[i],make_pair(r[i],k[i]))]=qwq;
fw(qwq);
__PC('\n');
}
}
else if(!(op[i]^3))
{
flg=1;
change(l[i],k[i]);
}
else if(!(op[i]^4))
{
fw(front(l[i],r[i],k[i]));
__PC('\n');
}
else
{
fw(back(l[i],r[i],k[i]));
__PC('\n');
}
}
fs();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探