P10633 BZOJ2989 数列/BZOJ4170 极光
P10633 BZOJ2989 数列/BZOJ4170 极光
转化出:
\[\begin{cases}
-k\leq a_x+x-(a_y+y)\le k \\
-k\leq x-a_x-(y-a_y)\le k
\end{cases}
\]
记 :
\[u=a_x+x, u'=a_y+y \\
v=x-a_x,v'=y-a_y
\]
转化为:
\[\begin{cases}
-k\leq u-u'\le k \\
-k\leq v-v'\le k
\end{cases}
\]
其中我们对 \(u,v\) 统计答案,\(u\) 可作为第一层坐标, \(v\) 作为第二层,范围为:
\[\begin{cases}
u-k\leq u'\le u+k \\
v-k\leq v'\le v+k
\end{cases}
\]
区间查询即可。我这里有外层普通线段树/动态开点值域线段树,内层动态开点值域线段树两个版本。任君挑选。
外层普通线段树
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=6e5+10,m=2e5;
int n,q;
struct Node{
int sm=0/*该区间有多少点,仅在内层有用*/,ls=0,rs=0;
}tr[m<<2],tr1[(int)1.6e7];
int cnt=0,cnt1=0,id[(int)1.6e7];
void add1(int &x,int l,int r,int tar1){
if(!x) x=++cnt1;
tr1[x].sm++;
if(l==r) return ;
int mid=l+(r-l)/2;
if(tar1<=mid) add1(tr1[x].ls,l,mid,tar1);
else add1(tr1[x].rs,mid+1,r,tar1);
}
void add(int x,int l,int r,int tar1,int tar2){
add1(id[x],-m,m,tar2);
if(l==r)return ;
int mid=l+(r-l)/2;
if(tar1<=mid) add(x<<1,l,mid,tar1,tar2);
else add(x<<1|1,mid+1,r,tar1,tar2);
}
int query1(int x,int l,int r,int ql,int qr){
if(!x||qr<l||ql>r)return 0;
if(ql<=l&&r<=qr)return tr1[x].sm;
int mid=l+(r-l)/2;
return query1(tr1[x].ls,l,mid,ql,qr)+
query1(tr1[x].rs,mid+1,r,ql,qr);
}
int query(int x,int l,int r,int ql,int qr,int ql_,int qr_){
if(qr<l||ql>r)return 0;
if(ql<=l&&r<=qr)return query1(id[x],-m,m,ql_,qr_);
int mid=l+(r-l)/2;
return query(x<<1,l,mid,ql,qr,ql_,qr_)+
query(x<<1|1,mid+1,r,ql,qr,ql_,qr_);
}
int a[N];
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>q;
for(int i=1;i<=n;++i){
cin>>a[i];
int u= a[i]+i,v=i-a[i];
add(1,1,m,u,v);
}
while(q--){
string s;
int x,k;
cin>>s>>x>>k;
if(s=="Modify"){
a[x]=k;
add(1,1,m,a[x]+x,x-a[x]);
}else {
int u= a[x]+x,v=x-a[x];
int ans=query(1,1,m,max(1ll,u-k),min(m,u+k),max(-m,v-k),min(m,v+k));
cout<<ans<<"\n";
}
}
return 0;
}
/*
外层维护u 内层维护v
*/
外层动态开点
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int m=2e5,N=1.2e5+10;
int n,q;
struct Node{
int sm=0,ls=0,rs=0;
}tr[(int)1.6e7],tr1[(int)1.6e7];
int cnt=0,cnt1=0,rt=0,id[(int)1.6e7+10];
void add1(int &x,int l,int r,int tar1){
if(!x) x=++cnt1;
tr1[x].sm++;
if(l==r) return ;
int mid=l+(r-l)/2;
if(tar1<=mid) add1(tr1[x].ls,l,mid,tar1);
else add1(tr1[x].rs,mid+1,r,tar1);
}
void add(int &x,int l,int r,int tar1,int tar2){
if(!x) x=++cnt;
add1(id[x],-m,m,tar2);
if(l==r)return ;
int mid=l+(r-l)/2;
if(tar1<=mid) add(tr[x].ls,l,mid,tar1,tar2);
else add(tr[x].rs,mid+1,r,tar1,tar2);
}
int query1(int x,int l,int r,int ql,int qr){
if(!x||qr<l||ql>r)return 0;
if(ql<=l&&r<=qr)return tr1[x].sm;
int mid=l+(r-l)/2;
return query1(tr1[x].ls,l,mid,ql,qr)+
query1(tr1[x].rs,mid+1,r,ql,qr);
}
int query(int x,int l,int r,int ql,int qr,int ql_,int qr_){
if(!x||qr<l||ql>r)return 0;
if(ql<=l&&r<=qr)return query1(id[x],-m,m,ql_,qr_);
int mid=l+(r-l)/2;
return query(tr[x].ls,l,mid,ql,qr,ql_,qr_)+
query(tr[x].rs,mid+1,r,ql,qr,ql_,qr_);
}
int a[N];
signed main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>q;
int rt=0;
for(int i=1;i<=n;++i){
cin>>a[i];
int u= a[i]+i,v=i-a[i];
add(rt,1,m,u,v);
}
while(q--){
string s;
int x,k;
cin>>s>>x>>k;
if(s=="Modify"){
a[x]=k;
add(rt,1,m,a[x]+x,x-a[x]);
}else {
int u= a[x]+x ,v=x-a[x];
int ans=query(1,1,m,max(1ll,u-k),min(m,u+k),max(-m,v-k),min(m,v+k));
cout<<ans<<"\n";
}
}
return 0;
}
注意
需要注意的是,我们进行 add1 的时候:
void add1(int &x,int l,int r,int tar1){
if(!x) x=++cnt1;
tr1[x].sm++;
if(l==r) return ;
int mid=l+(r-l)/2;
if(tar1<=mid) add1(tr1[x].ls,l,mid,tar1);
else add1(tr1[x].rs,mid+1,r,tar1);
}
(可能)千万不要这样:
void add1(int &x,int l,int r,int tar1){
if(!x) x=++cnt1;
if(l==r) {
tr1[x].sm++;
return ;
}
int mid=l+(r-l)/2;
if(tar1<=mid) add1(tr1[x].ls,l,mid,tar1);
else add1(tr1[x].rs,mid+1,r,tar1);
if(!tr1[x].ls)tr1[x].ls=++cnt1;
if(!tr1[x].rs)tr1[x].rs=++cnt1;
tr1[x].sm=tr1[tr1[x].ls].sm+tr1[tr1[x].rs].sm;
}
可能会 TLE。
还有就是注意询问的值域,\(u\) 的范围不会涉及负数。询问超过值域没有意义。
当然我解释一下代码?
就是外层维护某一段范围的 \(u\),进行单点插入,找到特定的 \(u\) 后内层单点插入 \(v\)。
查询为区间查询,外层查找某段区间特定的 \(u\),对各个 \(u\) 对应的内层内特定区间的 \(v\) 进行区间数点。
煎蛋。

浙公网安备 33010602011771号