年轻人的第一次树套树
首先是一道经典的动态区间第k大。
先回想一下静态区间第k大的做法,主席树维护前缀的权值线段树,查询时在
l
−
1
l-1
l−1 和
r
r
r 两颗权值线段树上一起二分。
动态问题无非是添加了一个修改步骤,而如果要暴力修改的话需要对
i
i
i 到
n
n
n 的线段树都进行修改,复杂度来到
n
2
l
o
g
n
n^2logn
n2logn 。
考虑用树状数组维护对应区间的权值线段树,每次修改对
l
o
g
n
logn
logn 级别数量的线段树进行修改,查询时在
l
o
g
n
logn
logn 级别数量线段树上同时二分,因此整体复杂度
O
(
n
l
o
g
n
l
o
g
n
)
O(n logn logn)
O(nlognlogn)。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct T{
char c;
int a,b,k;
}q[maxn];
int b[3*maxn],num[maxn];
int n,m;
int rt[maxn];
class ptree{public:
#define nd node[now]
#define ndp node[pre]
#define mid (s+t)/2
int cnt;
struct segnode{
int l,r,sum;
}node[maxn*500];
void update(int pos, int val, int s, int t, int &now, int pre){
if(!now) now = ++cnt;
nd = ndp, nd.sum += val;
if(s == t) return ;
if(pos <= mid) update(pos, val, s, mid, nd.l, ndp.l);
else update(pos, val, mid+1, t, nd.r, ndp.r);
}
#undef mid
}tree1;
class bit{public:
int lb(int x){return x&(-x);}
void update(int pos,int x,long long val){
if(pos>0)
for(int i=pos;i<=n;i+=lb(i)){
tree1.update(x,val,1,b[0],rt[i],rt[i]);
}
}
int rtl[maxn],rtr[maxn];
int query(int l,int r,int k){
int s=1,t=b[0];
int mid,sum;
for(int i=l-1;i;i-=lb(i)) rtl[i]=rt[i];
for(int i=r;i;i-=lb(i)) rtr[i]=rt[i];
while(s<t){
mid=(s+t)/2,sum=0;
for(int i=l-1;i;i-=lb(i)) sum-=tree1.node[tree1.node[rtl[i]].l].sum;
for(int i=r;i;i-=lb(i)) sum+=tree1.node[tree1.node[rtr[i]].l].sum;
if(sum>=k){
for(int i=l-1;i;i-=lb(i)) rtl[i]=tree1.node[rtl[i]].l;
for(int i=r;i;i-=lb(i)) rtr[i]=tree1.node[rtr[i]].l;
t=mid;
}
else{
for(int i=l-1;i;i-=lb(i)) rtl[i]=tree1.node[rtl[i]].r;
for(int i=r;i;i-=lb(i)) rtr[i]=tree1.node[rtr[i]].r;
k-=sum;
s=mid+1;
}
}
return s;
}
}tree2;
int main(){
// ios::sync_with_stdio(false);
// cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>num[i],b[++b[0]]=num[i];
for(int i=1;i<=m;i++){
cin>>q[i].c;
if(q[i].c=='Q') cin>>q[i].a>>q[i].b>>q[i].k;
else cin>>q[i].a>>q[i].b,b[++b[0]]=q[i].b;
}
sort(b+1,b+1+b[0]);
b[0]=unique(b+1,b+1+b[0])-b-1;
for(int i=1;i<=n;i++) num[i]=lower_bound(b+1,b+1+b[0],num[i])-b;
for(int i=1;i<=m;i++){
if(q[i].c=='C')
q[i].b=lower_bound(b+1,b+1+b[0],q[i].b)-b;
}
for(int i=1;i<=n;i++) tree2.update(i,num[i],1);
for(int i=1;i<=m;i++){
if(q[i].c=='Q'){
int l=q[i].a, r=q[i].b, k=q[i].k;
cout<<b[tree2.query(l,r,k)]<<endl;
}
else{
int temp=q[i].a,to=q[i].b;
tree2.update(temp,num[temp],-1);
num[temp]=to;
tree2.update(temp,num[temp],1);
}
}
}
第二题是2019南昌icpc网络赛的i题。
题目大意:
给定一个序列a,支持单点修改,询问区间中值域在一定范围内的最长连续段的个数。
例如序列 3 3 1 5 6 5, 区间[1,3],值域在[1,3]范围的最长连续段个数为2,分别为 [1,2] 和 [3,3]。
思路:
首先如果去掉修改操作,主席树维护前缀值域区间的序列最长连续段个数,按下标建树即可,查询时直接查询值域 y 和值域 x-1 的区间然后相减即可。
加上修改操作,加上树状数组之后主席树按照树状数组的区间值域建树即可。具体实现查询序列最长连续段个数的方法是,每个节点维护最长连续段个数和左端点、右端点的值,两个区间如果端点值相同,合并后最长连续段个数等于两个区间的最长连续段个数和-1,否则不-1。
然而写完之后发现空间开不下。。。 等我学完cdq再来吧:)
代码先放在这里
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m;
int rt[maxn];
class ptree{public:
#define nd node[now]
#define ndp node[pre]
#define mid (l+r)/2
int cnt;
struct segnode{
int l,r,sum,sl,sr;
}node[maxn*130];
void pushup(int now){
if(!nd.l) nd.sum=node[nd.r].sum,nd.sr=node[nd.r].sr,nd.sl=-1;
else if(!nd.r) nd.sum=node[nd.l].sum,nd.sl=node[nd.l].sl,nd.sr=-1;
else{
if(node[nd.l].sr==node[nd.r].sl) nd.sum=node[nd.l].sum+node[nd.r].sum-1;
else nd.sum=node[nd.l].sum+node[nd.r].sum;
nd.sl=node[nd.l].sl,nd.sr=node[nd.r].sr;
}
}
void update(int pos, int x, int l, int r, int &now, int pre){
if(!now) now = ++cnt;
nd=ndp;
if(l == r){
nd.sum=1;
nd.sl=x,nd.sr=x;
return;
}
if(pos <= mid) update(pos, x, l, mid, nd.l, ndp.l);
else update(pos, x, mid+1, r, nd.r, ndp.r);
pushup(now);
}
int query(int l,int r,int s,int t,int now){
if(s<=l && r<=t) return nd.sum;
char ll=-1;
int sum=0;
if(s<=mid && nd.l!=0) ll=node[nd.l].sr,sum+=query(l,mid,s,t,nd.l);
if(t>mid && nd.r!=0){
if(ll==node[nd.r].sl) sum+=query(mid+1,r,s,t,nd.r)-1;
else sum+=query(mid+1,r,s,t,nd.r);
}
return sum;
}
#undef mid
}tree1;
class bit{public:
int lb(int x){return x&(-x);}
void update(int pos,int t){
if(pos>0)
for(int i=pos;i<=n;i+=lb(i)){
tree1.update(t,pos,1,n,rt[i],rt[i]);
}
}
int query(int pos,int l,int r){
int sum=0;
for(int i=pos;i;i-=lb(i)){
sum+=tree1.query(1,n,l,r,rt[i]);
}
return sum;
}
int ask(int l,int r,int x,int y){
return query(y,l,r)-query(x-1,l,r);
}
}tree2;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;
cin>>x;
tree2.update(x,i);
}
for(int i=1;i<=m;i++){
int q;
cin>>q;
if(q==1){
int pos,v;
cin>>pos>>v;
tree2.update(v,pos);
}
else{
int l,r,x,y;
cin>>l>>r>>x>>y;
cout<<tree2.ask(l,r,x,y)<<'\n';
}
}
}