数据结构-树状数组

博客推荐

1.https://blog.csdn.net/flushhip/article/details/79165701
2.https://www.luogu.org/blog/Chanis/super-BIT
3.https://www.bestsort.cn/2019/04/26/195
4.https://www.cnblogs.com/aininot260/p/9336527.html

模板

一维树状数组

单点修改,区间查询

/************************
User:Mandy.H.Y
Language:c++
Problem:loj130 Binary Index Tree 1
Algorithm:Binary Index Tree
Date:2019.7.10
Scores:100
************************/
//单点修改,区间查询
//c[i]表示从第i个元素向前数lowbit(i)个元素,这一段的和
#include<bits/stdc++.h>

using namespace std;

const int maxn=1e6+5;

int n,q;
long long sum[maxn]; 

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("130.in","r",stdin);
	freopen("130.out","w",stdout);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int pos,int val){//i的父亲节点是i+lowbit(i) 
	for(int i=pos;i<=n;i+=lowbit(i)) sum[i]+=(long long)val;
}

void readdata(){
	read(n);read(q);
	for(int i=1;i<=n;++i){
		int x;read(x);
		modify(i,x);
	}
}

long long query(int l,int r){
	long long suml=0,sumr=0;
	for(int i=r;i;i-=lowbit(i)) sumr+=(long long)sum[i];
	for(int i=l-1;i;i-=lowbit(i)) suml+=(long long)sum[i];//注意long long 
	return sumr-suml;
}

void work(){
	while(q--){
		int opt;
		read(opt);
		if(opt==1){
			int pos,val;read(pos);read(val);
			modify(pos,val);
		}else{
			int l,r;
			read(l);read(r);
			put(query(l,r));putchar('\n');
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

 

区间修改,单点查询

/********************
User:Mandy.H.Y
Language:c++
Problem:loj131 Binary Index Tree 2
Algorithm:Binary Index Tree 2
Date:2019.7.10
Scores:
********************/
//区间修改,单点查询
//差分 
//delta[i]=a[i]-a[i-1](a为原数组) 
//把delta[i]整合为树状数组
//则delta[i]表示从第i个元素向前数lowbit(i)个元素,这一段的和 
#include<bits/stdc++.h>

using namespace std;

const int maxn=1e6+5;

int n,q;
long long delta[maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("131.in","r",stdin);
	freopen("131.out","w",stdout);
}

int lowbit(int x){
	return x & (-x);
} 

void modify(int l,int r,int val){
	for(int i=r+1;i<=n;i+=lowbit(i)) delta[i]-=(long long)val;
	for(int i=l;i<=n;i+=lowbit(i)) delta[i]+=(long long)val;
}

long long query(int pos){
	long long sum=0;
	for(int i=pos;i;i-=lowbit(i)) sum+=delta[i];
	return sum;
}

void readdata(){
	read(n);read(q);
	int x;
	for(int i=1;i<=n;++i){
		read(x);
		modify(i,i,x);//这里直接加值,相当于把[i,i]这个区间所有值加上x 
	}
}

void work(){
	while(q--){
		int opt;read(opt);
		if(opt==1){
			int l,r,x;read(l);read(r);read(x);
			modify(l,r,x); 
		}else{
			int pos;read(pos);
			put(query(pos));putchar('\n');
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

//第二种方法 
/*
User:Mandy.H.Y
Language:c++
Problem:
*/

/*
//c[i]表示从第i个元素向前数lowbit(i)个元素,这一段的和
#include<bits/stdc++.h>

#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define mem(A) memset((A),0,sizeof(A))

using namespace std;

const int maxn=1e6+5;

int n,q;
long long sum[maxn];

template<typename T>inline void read(T &x)
{
	x=0;char c=getchar();bool f=0;
	while(c<'0'||c>'9') {f|=(c=='-');c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	if(f) x=-x;
}

template<typename T>void putch(const T x)
{
	if(x>9) putch(x/10);
	putchar((x%10)|48);
}

template<typename T>inline void put(const T x)
{
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void docu()
{
	freopen("pasture.txt","r",stdin);
}

int lowbit(int x)
{
	return x&(-x);
}

void modify(int k,int x)
{
	for(int i=k;i<=n;i+=lowbit(i))
	{
		sum[i]+=(long long)x;
	}
}

void query(int r)
{
	long long sum1=0;
	for(int i=r;i>0;i-=lowbit(i)) sum1+=sum[i];
	printf("%lld\n",sum1);
}

void readdata()
{
	read(n);read(q);
	int pre=0;
	for(int i=1;i<=n;++i) 
	{
		int x;
		read(x);
		modify(i,x-pre);
		pre=x;
	}
	
}

void work()
{
	for(int i=1;i<=q;++i)
	{
		int judge,j,x,k;
		read(judge);
		
		if(judge==1)
		{
			read(j); read(k); read(x);
			modify(j,x);
			modify(k+1,-x);
		}
		else
		{
			read(x);
			query(x);
		}
	}
}

int main()
{
//	docu();
	readdata(); 
	work();
	return 0;
}

*/

区间修改,区间查询

/*************************
User:Mandy.H.Y
Language:c++
Problem:loj132 Binary Index Tree 3
Algorithm:
Date:2019.7.10
Scores: 
*************************/
//区间修改,区间查询

//设d[i]=a[i]-a[i-1];
//则sum[i]=d[j]*(i-j+1)(j= 1 to i)
//sum[i]=d[j]*(i+1)-d[j]*j (j= 1 to i)
//把d整合为树状数组,修改时向父节点推 

#include<bits/stdc++.h>

using namespace std;

const int maxn=1e6+5;

int n,q;
long long sum[maxn],delta1[maxn],delta2[maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("132.in","r",stdin);
	freopen("132.out","w",stdout);
}

void readdata(){
	read(n);read(q);
	for(int i=1;i<=n;++i){
		int x;read(x);
		sum[i]=sum[i-1]+x;//事先求出sum,delta 中只存改变量 
	}
}

int lowbit(int x){
	return x&(-x);
}

void add(int pos,int val){
	long long mval=(long long)val*(long long)pos;
	for(int i=pos;i<=n;i+=lowbit(i)){
		delta1[i]+=val;
		delta2[i]+=mval;
	}//向上
}

long long query(int pos){
	long long sum1=0,sum2=0;
	for(int i=pos;i;i-=lowbit(i)){
		sum1+=delta1[i];
		sum2+=delta2[i];
	}
	return sum[pos]+sum1*(pos+1)-sum2; 
}

void work(){
	while(q--){
		int opt,l,r,x;read(opt);
		if(opt==1){
			read(l);read(r);read(x);
			add(l,x);add(r+1,-x);
		}else{
			read(l);read(r);
			put(query(r)-query(l-1));
			putchar('\n');
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

二维树状数组

单点修改,区间查询

/************************
User:Mandy.H.Y
Language:c++
Problem:
Algorithm:
Date:
Score:
************************/

//单点修改,区间查询 

#include<bits/stdc++.h>

using namespace std;

const int maxn = (1 << 12) + 5;

int n,m;
long long a[maxn][maxn];

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
	while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag)x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("1.in","r",stdin);	
	freopen("133.out","w",stdout);	
}

void readdata(){
	read(n);read(m);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int x,int y,long long val){
	for(int i = x;i <= n;i += lowbit(i))
			a[i][j] += val;
}

long long query(int x,int y){
	long long sum=0;
	for(int i = x;i;i -= lowbit(i))
		for(int j = y;j;j -= lowbit(j))
			sum += a[i][j];
	return sum;
}

void work(){
	int opt,a,b,c,d;
	long long delta;
	while(~scanf("%d",&opt)){
		if(opt == 1){
			read(a);read(b);read(delta);
			modify(a,b,delta);		
		}else if(opt == 2){
			read(a);read(b);read(c);read(d);
			long long ans = query(c,d) - query(c,b - 1)
							- query(a - 1,d) + query(a - 1,b - 1);
			put(ans);putchar('\n');
		}
	}
}

int main(){
	file();
	readdata();
	work();
	return 0;
}

区间修改,单点查询 && 区间修改,区间查询

代码引自: 数据结构:二维树状数组、三维树状数组 BY静听风吟。
https://www.cnblogs.com/aininot260/p/9336527.html

//单点修改,区间查询 

#include<iostream>
using namespace std;
const int maxn=1005;
const int maxm=1005;
int n,m;
int q;
int a[maxn][maxm];
int c[maxn][maxm];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y,int z)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        c[i][j]+=z;
}

int sum(int x,int y)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=c[i][j];
    return ret;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        cin>>a[i][j];
        update(i,j,a[i][j]);
    }
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int y,z,w;
            cin>>y>>z>>w;
            update(y,z,w);
        }
        if(x==2)
        {
            int x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            cout<<sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1)<<endl;
        }
    }
    return 0;
}

//区间修改,单点查询

 #include<iostream>
using namespace std;
const int maxn=1005;
const int maxm=1005;
int n,m;
int q;
int a[maxn][maxm];
int c[maxn][maxm];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y,int z)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        c[i][j]+=z;
}

int sum(int x,int y)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=c[i][j];
    return ret;
}
int main()
{
    cin>>n>>m;
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int x1,y1,x2,y2,w;
            cin>>x1>>y1>>x2>>y2>>w;
            update(x1,y1,w);
            update(x2+1,y2+1,w);
            update(x2+1,y1,-w);
            update(x1,y2+1,-w);
        }
        if(x==2)
        {
            int x,y;
            cin>>x>>y;
            cout<<sum(x,y)<<endl;
        }
    }
    return 0;
}

//区间修改,区间查询

//这样快一点

#include<bits/stdc++.h>

using namespace std;

const int maxn = (1 << 11) + 5;

int n,m;
int a1[maxn][maxn];
int a2[maxn][maxn];
int a3[maxn][maxn];
int a4[maxn][maxn];//开long long 会爆空间 

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
	while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag)x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("4514.in","r",stdin);	
	freopen("4514.out","w",stdout);	
}

void readdata(){
	read(n);read(m);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int x,int y,int val){
	for(int i = x;i <= n;i += lowbit(i))
		for(int j = y;j <= m;j += lowbit(j)){
			a1[i][j] += val;
			a2[i][j] += val * x;
			a3[i][j] += val * y;
			a4[i][j] += val * x * y;
		}
			
}

int query(int x,int y){
	int sum1 = 0,sum2 = 0,sum3 = 0,sum4 = 0;
	for(int i = x;i;i -= lowbit(i))
		for(int j = y;j;j -= lowbit(j)){
			sum1 += a1[i][j];
			sum2 += a2[i][j];
			sum3 += a3[i][j];
			sum4 += a4[i][j];//一起算,否则会爆时间 
		}
	return sum1 * (x + 1) * (y + 1) -
		   sum2 * (y + 1) -
		   sum3 * (x + 1) +
		   sum4;
}

void work(){
	char opt;
	int a,b,c,d;
	int delta;
	while(~scanf("%c",&opt)){
		if(opt == 'L'){
			read(a);read(b);read(c);read(d);read(delta);
			modify(a,b,delta);
			modify(c + 1,d + 1,delta);
			modify(a,d + 1,-delta);
			modify(c + 1,b,-delta);
		}else if(opt == 'k'){
			read(a);read(b);read(c);read(d);
			int ans1 = query(c,d);
			int ans2 = query(c,b - 1);
			int ans3 = query(a - 1,d);
			int ans4 = query(a - 1,b - 1);
			put(ans1 - ans2 - ans3 + ans4);putchar('\n');
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}
//这样也行
#include<iostream>
using namespace std;
const int maxn=1005;
const int maxm=1005;
int n,m;
int q;
int a[maxn][maxm];
int c1[maxn][maxm];
int c2[maxn][maxm];
int c3[maxn][maxm];
int c4[maxn][maxm];
int lowbit(int x)
{
    return x&(-x);
}
void update(int c[][maxm],int x,int y,int z)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
        c[i][j]+=z;
}

int sum(int c[][maxm],int x,int y)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
        ret+=c[i][j];
    return ret;
}

int get(int x,int y)
{
    return sum(c1,x,y)*(x+1)*(y+1)-sum(c2,x,y)*(y+1)-(x+1)*sum(c3,x,y)+sum(c4,x,y);
}
int main()
{
    cin>>n>>m;
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int x1,y1,x2,y2,w;
            cin>>x1>>y1>>x2>>y2>>w;
            update(c1,x1,y1,w),update(c1,x1,y2+1,-w);
            update(c1,x2+1,y1,-w),update(c1,x2+1,y2+1,w);
        
            update(c2,x1,y1,w*x1),update(c2,x2+1,y1,-w*(x2+1));
            update(c2,x1,y2+1,-w*x1),update(c2,x2+1,y2+1,w*(x2+1));
        
            update(c3,x1,y1,w*y1),update(c3,x2+1,y1,-w*y1);
            update(c3,x1,y2+1,-w*(y2+1)),update(c3,x2+1,y2+1,w*(y2+1));
        
            update(c4,x1,y1,w*x1*y1),update(c4,x2+1,y1,-w*(x2+1)*y1);
            update(c4,x1,y2+1,-w*x1*(y2+1)),update(c4,x2+1,y2+1,w*(x2+1)*(y2+1));
        }
        if(x==2)
        {
            int x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            cout<<get(x2,y2)-get(x2,y1-1)-get(x1-1,y2)+get(x1-1,y1-1)<<endl;
        }
    }
    return 0;
}

//三维树状数组修改区间查询点

#include<iostream>
using namespace std;
const int maxn=105;
const int maxm=105;
const int maxl=105;
int n,m,l;
int q;
int a[maxn][maxm][maxl];
int c[maxn][maxm][maxl];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y,int z,int w)
{
    for(int i=x;i<=n;i+=lowbit(i))
    for(int j=y;j<=m;j+=lowbit(j))
    for(int k=z;k<=l;k+=lowbit(k))
        c[i][j][k]+=w;
}

int sum(int x,int y,int z)
{
    int ret=0;
    for(int i=x;i>=1;i-=lowbit(i))
    for(int j=y;j>=1;j-=lowbit(j))
    for(int k=z;k>=1;k-=lowbit(k))
        ret+=c[i][j][k];
    return ret;
}
int main()
{
    cin>>n>>m>>l;
    cin>>q;
    while(q--)
    {
        int x;
        cin>>x;
        if(x==1)
        {
            int x1,y1,z1,x2,y2,z2,w;
            cin>>x1>>y1>>z1>>x2>>y2>>z2>>w;
             update(x1,y1,z1,w); 
             update(x1,y2+1,z1,-w); 
             update(x2+1,y1,z1,-w); 
             update(x2+1,y2+1,z1,w); 

             update(x1,y1,z2+1,-w); 
             update(x1,y2+1,z2+1,w); 
             update(x2+1,y1,z2+1,w); 
             update(x2+1,y2+1,z2+1,-w); 
        }
        if(x==2)
        {
            int x,y,z;
            cin>>x>>y>>z;
            cout<<sum(x,y,z)<<endl;
        }
    }
    return 0;
} 

练习

校门外的树

题目传送:loj10115vijos1448

描述

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
K=2,读入l,r表示询问l~r之间能见到多少种树
(l,r>0)
格式

输入格式

第一行n,m表示道路总长为n,共有m个操作
接下来m行为m个操作
输出格式

对于每个k=2输出一个答案
样例1

样例输入1

5 4
1 1 3
2 2 5
1 2 4
2 3 5
Copy
样例输出1

1
2
Copy
限制

1s
提示

范围:20%的数据保证,n,m<=100
60%的数据保证,n <=1000,m<=50000
100%的数据保证,n,m<=50000
来源

dejiyu@CSC WorkGroup

/*************************
User:Mandy.H.Y
Language:c++
Problem:loj10115 tree
Algorithm:
Date:2019.7.13
Scores:
*************************/

//种下一排树,相当于给区间的起点和终点加括号 
//可以把数书的种类转化为数括号的个数 
// r左边的'['数表示开头到r中树的种类数
// l左边的']'数代表l的左边失去了多少种树 
#include<bits/stdc++.h>

using namespace std;

const int maxn=5e4+5;

int n,m;
long long tree[3][maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar(); 
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x; 
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("10115.in","r",stdin);
}

void readdata(){
	read(n);read(m);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int pos,int x,int id){
	for(int i=pos;i<=n;i+=lowbit(i)) tree[id][i]+=x; 
}

int query(int pos,int id){
	int sum=0;
	for(int i=pos;i;i-=lowbit(i)) sum+=tree[id][i];
	return sum; 
}

void work(){
	while(m--){
		int k,l,r;
		read(k);read(l);read(r);
		if(k==1){
			modify(l,1,0);
			modify(r,1,1);
		}
		else{
			int sum1=query(r,0);
			int sum2=query(l-1,1);//l-1
			put(sum1-sum2);
			putchar('\n'); 
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

数星星(Stars)

题目传送:loj10114Ural1028

天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。如果一个星星的左下方(包含正左和正下)有 k 颗星星,就说这颗星星是 k 级的。

在这里插入图片描述

例如,上图中星星 5 是 3级的( 1,2,4在它左下),星星 2,4 是 1 级的。例图中有 1 个 0 级, 2个 1级, 1个 2 级,1 个3 级的星星。

给定星星的位置,输出各级星星的数目。

一句话题意 给定 n 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。

输入格式
第一行一个整数N ,表示星星的数目;
接下来 N行给出每颗星星的坐标,坐标用两个整数 x,y 表示;
不会有星星重叠。星星按 y坐标增序给出, y坐标相同的按 x 坐标增序给出。

输出格式
N行,每行一个整数,分别是 0级,1 级,2 级,……,N-1 级的星星的数目。
样例
样例输入

5
1 1
5 1
7 1
3 3
5 5
样例输出

1
2
1
1
0
数据范围与提示
对于全部数据 1 <= N <= 1.5* 10 4 , 0 <= x,y <= 3.2 * 104.

/**********************
User:Mandy.H.Y
Language:c++
Problem:loj0114 Stars
Algorithm:树状数组 
Date:2019.7.13
Scores: 
**********************/
//嗯,坐标轴向上向右为正方向 
//本来是按顺序输入,则只用一维即可 
#include<bits/stdc++.h>

using namespace std;

const int maxn=2e4+5;

int n,tree[32005],ans[maxn];
struct Node{
	int x,y;
}node[maxn];


template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("10114.in","r",stdin);
}

void readdata(){
	read(n);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int pos){
	for(int i=pos;i<=32001;i+=lowbit(i)) tree[i]++;
}

int query(int pos){
	int sum=0;
	for(int i=pos;i;i-=lowbit(i)) sum+=tree[i];
	return sum;
}

void work(){
	for(int i=1;i<=n;++i){
		read(node[i].x);read(node[i].y);
		node[i].x++;
		//听说树状数组无法维护下标为零的元素  >_< 
		int cur=query(node[i].x);
		ans[cur]++;
		modify(node[i].x);
	}	
	
	for(int i=0;i<n;++i) put(ans[i]),putchar('\n');
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

[SDOI2009]HH的项链

题目传送:luogu1972

题目背景

题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入输出格式

输入格式:
第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:
M 行,每行一个整数,依次表示询问对应的答案。

输入输出样例

输入样例#1: 复制
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例#1: 复制
2
2
4
说明

数据范围:

对于100%的数据,N <= 500000,M <= 500000。

/**********************
User:Mandy.H.Y
Language:c++
Problem:luogu 1972 HH
Algorithm:
Date:2019.7.20
Scores:
**********************/
//不能单步调试的电脑不是好电脑 
#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e5 + 5;
const int maxm = 1e6 + 5;

int n,m,a[maxn];
int ans[maxn];
int pos[maxm];
int tree[maxn],pre[maxn];

struct Query{
	int l,r,h;
}q[maxn];

template<class T>inline void read(T &x) {
	x = 0;
	bool flag = 0;
	char ch = getchar();
	while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
	while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag) x = -x;
}

template<class T>void putch(const T x) {
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x) {
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void file() {
	freopen("1972.in","r",stdin);
	freopen("1972.out","w",stdout);
}

void readdata() {
	read(n);
	for(int i = 1; i <= n; ++ i) read(a[i]);
	read(m);
	for(int i = 1; i <= m; ++ i)
		read(q[i].l),read(q[i].r),q[i].h = i;
		//离线操作 
}

bool cmp(const Query &x,const Query &y) {
	if(x.r == y.r) return x.l > y.l;
	else return x.r > y.r;
}

int lowbit(int x){
	return x & (-x);
}

void modify(int pos,int val){
	for(int i = pos;i <= n;i += lowbit(i)){
		tree[i] += val;
	}
}

int query(int pos){
	int sum = 0;
	
	for(int i = pos;i;i -= lowbit(i)){
		sum += tree[i];
	}
	
	return sum;
}

void init(){
	
	for(int i = 1;i <= n; ++ i){
		pre[i] = pos[a[i]];
		pos[a[i]] = i;
	}//记录下每一种贝壳的最后的位置并记录前一个与他相同的贝壳的位置 
	
	for(int i = n;i >= 1; -- i)
		if(pos[a[i]] == i) modify(i,1);
	//每种贝壳最后出现的位置在树状数组中 +1
	 
//	for(int i = 1;i <= n; ++ i) printf("query(%d) = %d\n",i,query(i));
}

void work() {
	
	sort(q + 1,q + m + 1,cmp);//从后往前排 
	
//	for(int i = 1;i <= m; ++ i) printf("q[%d].r = %d\n",i,q[i].r);
	
	init();
	
	int j = 1;
	
	for(int i = n;i >= 1; -- i) {
		
		
		while(q[j].r == i){//当枚举到询问的区间 

//			printf("q[%d].r = %d,query = %d,q[%d].l = %d,query = %d\n",j,q[j].r,query(q[j].r),j,q[j].l,query(q[j].l));
			//注意此处i,j; 
			ans[q[j].h] = query(q[j].r) - query(q[j].l - 1);
//			种类数 = r前的种类数 - (l - 1)前的种类数
 
			//注意 l - 1 
			++ j;
			//指标后移 
		}
		
		pos[a[i]] = pre[i];
		//这个点已访问过,不再有用,不会再被访问,种类数记在他的前驱身上	
		if(pos[a[i]]) modify(pos[a[i]],1);
			//树状数组不维护0节点 
	}
	
	for(int i = 1;i <= m; ++ i){
		put(ans[i]),putchar('\n');
	}//注意按照询问的顺序 
	
}

int main() {
//	file();
	readdata();
	work();
	return 0;
}

上帝造题的七分钟

题目传送:luogu4514

题目背景

裸体就意味着身体。

题目描述

“第一分钟,X说,要有矩阵,于是便有了一个里面写满了00的n×mn×m矩阵。
第二分钟,L说,要能修改,于是便有了将左上角为(a,b)(a,b),右下角为(c,d)(c,d)的一个矩形区域内的全部数字加上一个值的操作。
第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作。
第四分钟,彩虹喵说,要基于二叉树的数据结构,于是便有了数据范围。
第五分钟,和雪说,要有耐心,于是便有了时间限制。
第六分钟,吃钢琴男说,要省点事,于是便有了保证运算过程中及最终结果均不超过32位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。”
——《上帝造裸题的七分钟》
所以这个神圣的任务就交给你了。

输入输出格式

输入格式:
输入数据的第一行为X n m,代表矩阵大小为n×mn×m。
从输入数据的第二行开始到文件尾的每一行会出现以下两种操作:

L a b c d delta —— 代表将(a,b),(c,d)(a,b),(c,d)为顶点的矩形区域内的所有数字加上delta。
k a b c d —— 代表求(a,b),(c,d)(a,b),(c,d)为顶点的矩形区域内所有数字的和。
请注意,kk为小写。

输出格式:
针对每个k操作,在单独的一行输出答案。

输入输出样例

输入样例#1: 复制
X 4 4
L 1 1 3 3 2
L 2 2 4 4 1
k 2 2 3 3
输出样例#1: 复制
12
说明

对于10%的数据,1 ≤ n ≤ 16, 1 ≤ m ≤ 161≤n≤16,1≤m≤16, 操作不超过200个.
对于60%的数据,1 ≤ n ≤ 512, 1 ≤ m ≤ 5121≤n≤512,1≤m≤512.
对于100%的数据,1 ≤ n ≤ 2048, 1 ≤ m ≤ 2048, -500 ≤ delta ≤ 500,1≤n≤2048,1≤m≤2048,−500≤delta≤500,操作不超过200000个,保证运算过程中及最终结果均不超过32位带符号整数类型的表示范围。
by XLk

/************************
User:Mandy.H.Y
Language:c++
Problem:luogu4514 God
Algorithm:树状数组 
Date:2019.7.17
Score:100
************************/
#include<bits/stdc++.h>

using namespace std;

const int maxn = (1 << 11) + 5;

int n,m;
int a1[maxn][maxn];
int a2[maxn][maxn];
int a3[maxn][maxn];
int a4[maxn][maxn];//开long long 会爆空间 

template<class T>inline void read(T &x){
	x = 0;bool flag = 0;char ch = getchar();
	while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
	while( isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
	if(flag)x = -x;
}

template<class T>void putch(const T x){
	if(x > 9) putch(x / 10);
	putchar(x % 10 | 48);
}

template<class T>void put(const T x){
	if(x < 0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("4514.in","r",stdin);	
	freopen("4514.out","w",stdout);	
}

void readdata(){
	read(n);read(m);
}

int lowbit(int x){
	return x & (-x);
}

void modify(int x,int y,int val){
	for(int i = x;i <= n;i += lowbit(i))
		for(int j = y;j <= m;j += lowbit(j)){
			a1[i][j] += val;
			a2[i][j] += val * x;
			a3[i][j] += val * y;
			a4[i][j] += val * x * y;
		}
			
}

int query(int x,int y){
	int sum1 = 0,sum2 = 0,sum3 = 0,sum4 = 0;
	for(int i = x;i;i -= lowbit(i))
		for(int j = y;j;j -= lowbit(j)){
			sum1 += a1[i][j];
			sum2 += a2[i][j];
			sum3 += a3[i][j];
			sum4 += a4[i][j];//一起算,否则会爆时间 
		}
	return sum1 * (x + 1) * (y + 1) -
		   sum2 * (y + 1) -
		   sum3 * (x + 1) +
		   sum4;
}

void work(){
	char opt;
	int a,b,c,d;
	int delta;
	while(~scanf("%c",&opt)){
		if(opt == 'L'){
			read(a);read(b);read(c);read(d);read(delta);
			modify(a,b,delta);
			modify(c + 1,d + 1,delta);
			modify(a,d + 1,-delta);
			modify(c + 1,b,-delta);
		}else if(opt == 'k'){
			read(a);read(b);read(c);read(d);
			int ans1 = query(c,d);
			int ans2 = query(c,b - 1);
			int ans3 = query(a - 1,d);
			int ans4 = query(a - 1,b - 1);
			put(ans1 - ans2 - ans3 + ans4);putchar('\n');
		}
	}
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}
树状数组是一种用于加速前缀和操作的数据结构。它可以在O(logn)的时间复杂度内更新单个元素,并且可以在O(logn)的时间复杂度内查询一个区间的和。\[1\] 树状数组的基本思想是将数组分解成若干个长度为2的幂次的区间,每个区间的和都可以通过一系列区间和的累加得到。树状数组的每个节点都存储了一段区间的和,通过不断迭代lowbit()运算,可以得到从1到x之间的和。\[3\] 在树状数组的实现中,可以使用add()函数来更新单个元素的值,使用query()函数来查询一个区间的和。add()函数通过迭代lowbit()运算,将更新的值加到对应的节点上。query()函数通过计算两个前缀和的差值来得到一个区间的和。\[2\] 差分树状数组树状数组的一种变体,它可以用来求解区间最大值。差分树状数组的基本思想是将原始数组转化为差分数组,然后对差分数组建立树状数组。通过查询树状数组得到的前缀和,再加上差分数组的前缀和,就可以得到原始数组的区间最大值。\[2\] 综上所述,树状数组是一种用于加速前缀和操作的数据结构,可以在O(logn)的时间复杂度内更新单个元素和查询一个区间的和。差分树状数组树状数组的一种变体,用于求解区间最大值。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [RMQ问题--------树状数组](https://blog.csdn.net/weixin_43743711/article/details/107191842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值