Connecting...

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\) 进行区间数点。

煎蛋。

posted @ 2025-07-23 21:05  余亦宸  阅读(14)  评论(0)    收藏  举报