G - Smaller Sum

G - Smaller Sum

求区间内小于等于x的值之和

1.动态开点+主席树

好写维护一下区间和就好了

2.离散化+主席树

常数小

3.分块维护区间和以及有序,散块暴力,整块二分找位置

code

一。

// LUOGU_RID: 170820941
// Problem: G - Smaller Sum
// Contest: AtCoder - Japan Registry Services (JPRS) Programming Contest 2024 (AtCoder Beginner Contest 339)
// URL: https://atcoder.jp/contests/abc339/tasks/abc339_g
// Memory Limit: 1024 MB
// Time Limit: 3500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
constexpr int MX=1e9;
const int N=2e5+9;
int a[N];
//主席树
struct ZXSEG{
    struct node{
        int l,r;//左右儿子,关系
        ll sum;
    }seg[N*100];//n*(logn*3);logn+3向上取整
    #define tl(id) seg[id].l//宏定义简洁
    #define tr(id) seg[id].r
    int root[N],index;//根节点,节点数量
    bool inrange(int L,int R,int l,int r){return L>=l && R<=r;}
    bool outofrange(int L,int R,int l,int r){return l>R || L>r;}
    void reset(int n){//多组样例
        for(int i=1;i<=n;i++){
            root[i]=0;
        }
        index=0;
    }
    void build(int &id,int l,int r){//传引用,通过子空间id值给父空间的gx[0],gx[1]赋值
        id=++index;
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        build(tl(id),l,mid);
        build(tr(id),mid+1,r);
    }
    /*
    递归建立各个历史版本的线段树
    post是前一个版本的节点指针,curr是当前版本的节点指针
    curr是传引用,通过子空间curr值,给父空间的tl(curr),tr(curr)赋值
    */
    void insert(int post,int &curr,int l,int r,int v){
        seg[curr=++index]=seg[post];//赋值
        seg[curr].sum+=v;//更新
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        if(v<=mid){//在左边递归
            insert(tl(post),tl(curr),l,mid,v);
        }else{
            insert(tr(post),tr(curr),mid+1,r,v);
        }
    }
    /*
    找区间[l,r]内的第k小,插入r的历史版本,进行二分
    [l,r]==[1,r]-[1,l-1];
    */
    ll query(int post,int curr,int L,int R,int l,int r){
        if(inrange(L,R,l,r)){
        	return seg[curr].sum-seg[post].sum;
        }else if(!outofrange(L,R,l,r)){
        	int mid=(L+R)>>1;
        	return query(tl(post),tl(curr),L,mid,l,r)+query(tr(post),tr(curr),mid+1,R,l,r);
        }else{
        	return 0;
        }
    }
}t;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		t.insert(t.root[i-1],t.root[i],0,MX,a[i]);
	}
	int q;
	cin>>q;
	ll ans=0;
	for(int i=1;i<=q;i++){
		ll l,r,x;
		cin>>l>>r>>x;
		l^=ans,r^=ans,x^=ans;
		if(l>r){
			swap(l,r);
		}
		cout<<(ans=t.query(t.root[l-1],t.root[r],0,MX,0,x))<<'\n';
	}
	return 0;
    return 0;
}

二.

// LUOGU_RID: 170822545
// Problem: G - Smaller Sum
// Contest: AtCoder - Japan Registry Services (JPRS) Programming Contest 2024 (AtCoder Beginner Contest 339)
// URL: https://atcoder.jp/contests/abc339/tasks/abc339_g
// Memory Limit: 1024 MB
// Time Limit: 3500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+9;
vector<int> X;
int a[N];
//主席树
struct ZXSEG{
    struct node{
        int l,r;//左右儿子,关系
        ll sum;
    }seg[N<<5];//动态开点
    #define tl(id) seg[id].l//宏定义简洁
    #define tr(id) seg[id].r
    int root[N],index;//根节点,节点数量
    bool inrange(int L,int R,int l,int r){return L>=l && R<=r;}
    bool outofrange(int L,int R,int l,int r){return l>R || L>r;}
    void reset(int n){//多组样例
        for(int i=1;i<=n;i++){
            root[i]=0;
        }
        index=0;
    }
    void build(int &id,int l,int r){//传引用,通过子空间id值给父空间的gx[0],gx[1]赋值
        id=++index;
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        build(tl(id),l,mid);
        build(tr(id),mid+1,r);
    }
    /*
    递归建立各个历史版本的线段树
    post是前一个版本的节点指针,curr是当前版本的节点指针
    curr是传引用,通过子空间curr值,给父空间的tl(curr),tr(curr)赋值
    */
    void insert(int post,int &curr,int l,int r,int v){
        seg[curr=++index]=seg[post];//赋值
        seg[curr].sum+=X[v-1];//更新
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        if(v<=mid){//在左边递归
            insert(tl(post),tl(curr),l,mid,v);
        }else{
            insert(tr(post),tr(curr),mid+1,r,v);
        }
    }
    /*
    找区间[l,r]内的第k小,插入r的历史版本,进行二分
    [l,r]==[1,r]-[1,l-1];
    */
    ll ask(int post,int curr,int l,int r,int k){//区间k
        if(l==r){//
            return l;
        }
        int mid=(l+r)>>1;
        int s=seg[tl(curr)].sum-seg[tl(post)].sum;//变化信息
        if(k<=s){//找k的位置
            return ask(tl(post),tl(curr),l,mid,k);
        }else{
            return ask(tr(post),tr(curr),mid+1,r,k-s);//减去左边的
        }
    }
    ll query(int post,int curr,int L,int R,int l,int r){//求0-x的和
        if(inrange(L,R,l,r)){
            return seg[curr].sum-seg[post].sum;
        }else if(!outofrange(L,R,l,r)){
            int mid=(L+R)>>1;
            return query(tl(post),tl(curr),L,mid,l,r)+query(tr(post),tr(curr),mid+1,R,l,r);
        }else{
            return 0;
        }
    }
}t;
ll binary(ll x){return lower_bound(X.begin(),X.end(),x)-X.begin()+1;}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		X.push_back(a[i]);
	}
	sort(X.begin(),X.end());
	X.erase(unique(X.begin(),X.end()),X.end());
	int xn=X.size();
	for(int i=1;i<=n;i++){
		t.insert(t.root[i-1],t.root[i],1,xn,binary(a[i]));
	}
	int q;
	cin>>q;
	ll ans=0;
	for(int i=1;i<=q;i++){
		ll l,r,x;
		cin>>l>>r>>x;
		l^=ans,r^=ans,x^=ans;
		x=upper_bound(X.begin(),X.end(),x)-X.begin();
		cout<<(ans=t.query(t.root[l-1],t.root[r],1,xn,1,x))<<'\n';
	}
	return 0;
}

三.

// LUOGU_RID: 170829733
// Problem: G - Smaller Sum
// Contest: AtCoder - Japan Registry Services (JPRS) Programming Contest 2024 (AtCoder Beginner Contest 339)
// URL: https://atcoder.jp/contests/abc339/tasks/abc339_g
// Memory Limit: 1024 MB
// Time Limit: 3500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+9;
int a[N];
struct BLOCK{
	//每个块的范围,[l,r]属于那个块
	int L[N],R[N],pos[N];
	//一个块的信息
	vector<ll> prefix[N];
	vector<int> v[N];
	int t=0;
	void init(int n){
		t=sqrt(n);
		for(int i=1;i<=t;i++){
			L[i]=(i-1)*t+1;
			R[i]=i*t;
		}
		if(R[t]<n){
			t++;
			L[t]=R[t-1]+1;
			R[t]=n;	
		}
		for(int i=1;i<=t;i++){
			for(int j=L[i];j<=R[i];j++){
				pos[j]=i;
			}
		}
		for(int i=1;i<=t;i++){
			for(int j=L[i];j<=R[i];j++){
				v[i].push_back(a[j]);
			}
		}
		for(int i=1;i<=t;i++){
			sort(v[i].begin(),v[i].end());
		}
		for(int i=1;i<=t;i++){
			ll t=0;
			prefix[i].push_back(t);
			for(auto & x : v[i]){
				prefix[i].push_back(t+=x);
			}
		}
		// for(int i=1;i<=t;i++){
			// for(auto & x : v[i]){
				// cout<<x<<" ";
			// }
			// cout<<'\n';
		// }
		// cout<<'\n';
		// cout<<'\n';
		// cout<<'\n';
		// cout<<'\n';
		// for(int i=1;i<=t;i++){
			// for(auto & x : prefix[i]){
				// cout<<x<<" ";
			// }
			// cout<<'\n';
		// }
	}
	ll query(ll l,ll r,ll x){
		int p=pos[l];
		int q=pos[r];
		ll res=0;
		if(p==q){
			for(int i=l;i<=r;i++){
				if(a[i]<=x){
					res+=a[i];
				}
			}
		}else{
			for(int i=l;i<=R[p];i++){
				if(a[i]<=x){
					res+=a[i];
				}
			}
			for(int i=p+1;i<=q-1;i++){
				int p=upper_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
				// cout<<"sadasdadad"<<" "<<p<<'\n';
				if(!p){
					continue;
				}else{
					res+=prefix[i][p];
				}
			}
			for(int i=L[q];i<=r;i++){
				if(a[i]<=x){
					res+=a[i];
				}
			}
		}
		return res;
	}
}blo;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	blo.init(n);
	// for(int i=1;i<=n;i++){
		// cout<<blo.prefix[i]<<" ";
	// }
	// cout<<'\n';
	int q;
	cin>>q;
	ll ans=0;
	for(int i=1;i<=q;i++){
		ll l,r,x;
		cin>>l>>r>>x;
		l^=ans,r^=ans,x^=ans;
		// cout<<l<<' '<<r<<' '<<" "<<x<<'\n';
		cout<<(ans=blo.query(l,r,x))<<'\n';
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值