1535:【例 1】数列操作

模板题

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=101000;
const int INF=0x3fffffff;
typedef long long LL;
#define lowbit(x)  ((x)&(-x))
int tree[maxn];
int n,m;
void add(int x,int d){
	while(x<=n){
		tree[x]+=d;
		x+=lowbit(x);
	}
}
LL getsu(int x){
	LL ans=0;
	while(x>0){
		ans+=tree[x];
		x-=lowbit(x);
	}
	return ans;
}
int main(){
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) {
		int x;
		scanf("%d",&x);
		add(i,x);
	}
	int k,a,b;
	while(m--){
		scanf("%d %d %d",&k,&a,&b);
		if(k==0) printf("%lld\n",getsu(b)-getsu(a-1));
		else add(a,b);
	}
return 0;
}

  

1536:【例 2】数星星 Stars

//二维的树状数组bushi
//认真读题呀!!!!!看给出数据的特征,是按照纵坐标从小到大排序的,纵坐标相同的是横坐标从小到大给出的
//也就是说,我们可以不管纵坐标,按照它给出的横坐标依次插入,并统计当前星星之前的横坐标小于它的星星个数。

观察输入数据的特点

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n;
int c[100005];
int ans[100055];
int maxn=32001;
struct node{
	int x,y;
}a[100005];
int lowbit(int x)
{
	return x&(-x);
}
void update(int x,int y)
{
	while(x<=maxn)
	{
		c[x]+=y;
		x+=lowbit(x);
	}
}
int sum(int x)
{
	int cnt=0;
	while(x>0)
	{
		cnt+=c[x];
		x-=lowbit(x);
	}
	return cnt;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	for(int i=1;i<=n;i++)
	{
		int wzx=a[i].x+1;
		int jd=sum(wzx);   //先计算个数,为什么不剪掉1,因为包括了正左、正下
		update(wzx,1);     //再进行更新
		ans[jd]++; 
	}
	for(int i=0;i<n;i++)
	printf("%d\n",ans[i]);
	return 0;
} 

  

1537:【例 3】校门外的树

 

 注意,这里不是点更新了,而是区间更新,区间查找,而对于区间更新:维护两个数组,一个负责开始,一个负责结尾

左右括号法。
对于每次操作[a,b],将位于a的左括号个数加一,位于b的右括号个数加一。
对于每次查询[a,b],定义X等于1到b的左括号个数,Y等于1到a−1的右括号个数,答案即为X−Y
https://blog.csdn.net/zhang14369/article/details/81071990

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=5e4+10; 
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int n,m;
int k;
//其他的做法:
//https://www.cnblogs.com/ECJTUACM-873284962/p/7060158.html 
//这道题需要维护两个 开两个数组来存一个是开始的点的数量,一个是结束的 ,然后随便搞一下,最后输出就可以了
int a[maxn],b[maxn];

int getsum(int l,int r){
	int ans=0;
	for(int i=r;i;i-=lowbit(i)) ans+=a[i];
	for(int i=l-1;i;i-=lowbit(i)) ans-=b[i];
	return ans;
}
int main(){
	scanf("%d %d",&n,&m);
	int l,r;
	for(int i=0;i<m;i++){
		scanf("%d %d %d",&k,&l,&r);
		if(k==1) {
				for(int i=l;i<=n+1;i+=lowbit(i)) a[i]++;
				for(int i=r;i<=n+1;i+=lowbit(i)) b[i]++;
			
		}
		else {
			printf("%d\n",getsum(l,r));
		}
	}
return 0;
}

  另一道区间求和、区间查询:利用差分的思想

1548:【例 2】A Simple Problem with Integers

 

 

 讲解:

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e6+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//这道题是模板题:区间求和、区间修改
//可以用线段树、也可以用树状数组
//感觉线段树简单一点,但是不好推
//用树状数组讲解:维护两个前缀和
//https://blog.csdn.net/gzcszzx/article/details/100539427
/*
维护两个前缀和,
S1[i]=d[i],S2[i]=d[i]*i
查询:位置Pos的前缀和就是(Pos+1)*S1中1到Pos的和 减去 S2中1到Pos的和,[L,R]=SS[R]-SS[L-1]
 
修改:[L,R] 
S1:S1[L]+Tag,S1[R+1]-Tag  
S2:S2[L]+Tag*L ,S2[R+1]-Tag*(R+1)
*/
LL n,m;
LL a[maxn],d[maxn];  //a[i]为原数组  d[i]为差分数组
LL c1[maxn],c2[maxn];  //两个前缀和
#define lowbit(x) ((x)&(-x))
void add(LL x,LL v){
    LL p=x;
    while(x<=n){
        c1[x]+=v;
        c2[x]+=p*v;
        x+=lowbit(x);
    }
}
LL getans(LL x){
    LL ans=0,p=x;
    while(x){
        ans+=(p+1)*c1[x]-c2[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    scanf("%lld %lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        d[i]=a[i]-a[i-1];
        add(i,d[i]);
    }
    while(m--){
        int p;
        scanf("%d",&p);
        if(p==1){
            LL l,r,c;
            scanf("%lld %lld %lld",&l,&r,&c);
            add(l,c);
            add(r+1,-c);
        }
        if(p==2){
            LL x,y;
            scanf("%lld %lld",&x,&y);
            printf("%lld\n",getans(y)-getans(x-1));
        }
    }
return 0;
}

  

1538:清点人数

模板题

数据时cin铁定超时,换用scanf问题就来了,用getchar()读回车不知为何会错,看到别人的博客,读字符是scanf(" %c",&op);这样的,前面多一个空格,也能解决换行符问题,记一下.

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=5e5+100;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int n,k;
char a;
int op[maxn];
void add(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))  op[i]+=k;
}
LL getans(int pos){
	LL ans=0;
	for(int i=pos;i;i-=lowbit(i)) ans+=op[i];
	return ans;
}
int main(){
	scanf("%d %d",&n,&k);
	//getchar();
	int m,p;
	for(int i=0;i<k;i++){
		scanf(" %c",&a);
		//读数据时cin铁定超时,换用scanf问题就来了,用getchar()读回车不知为何会错,看到别人的博客,读字符是scanf(" %c",&op);这样的,前面多一个空格,也能解决换行符问题,记一下.
		if(a=='A'){
			scanf("%d",&m);
			printf("%lld\n",getans(m));
		}
		else if(a=='B'){
			scanf("%d %d",&m,&p);
			add(m,p);
		}
		else if(a=='C'){
			scanf("%d %d",&m,&p);
			add(m,-p);
		}
		//getchar();
	}
return 0;
}

  

1539:简单题

 

 这题也是,区间更新,点查询-

表达的内容改变了:c[]记录的是这个位置的数改变了多少次,这道题也可以用线段树做

但是如果两种操作不反转的话:差分的思路??

树状数组维护差分数组
差分数组修改区间
只需要在区间左端点加上修改的值   add(l,1);
在右端点之后减去修改的值就好了  add(r+1,-1);
求某个位置上的值
就是这个位置之前(包括这个位置)的和   getans(l)%2
也符合树状数组里面的sum
就不需要做差了
最后按照%2来输出就好了
因为翻转两次之后会回到原来的情况

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=1e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//需要让数组01反转
//而且反转的是区间,
int  c[maxn];
//用C[i]表示对其区间表示范围内的元素修改了几次  !!!不要忘了这种啊 
//故可以用类似于树状数组中点修改的操作
int n,m;
//呜呜呜呜我错了,这是区间修改和点查询,两种的操作要反过来 
void add(int x,int k){
	while(x<=n){
		c[x]+=k;
		x+=lowbit(x);
	}
}
int getans(int x){
	int ans=0;
	while(x){
		ans+=c[x];
		x-=lowbit(x);
	}
	return ans;
}
int main(){
	scanf("%d %d",&n,&m);
	int t,l,r;
	while(m--){
		scanf("%d",&t);
		if(t==1){
			scanf("%d %d",&l,&r);
			add(l,1);
			add(r+1,-1);  //如果不反过来的话为甚恶魔是这样呢 ,减少一次更新的情况 
		}
		else if(t==2){
			scanf("%d",&l);
			printf("%d\n",getans(l)%2);
		}
	}
return 0;
}

  

1540:打鼹鼠_二维树状数组

 

 二维树状数组,最后一个点过不了

有两个注意的细节:

1)再add的时候,数据范围是x<=maxn,tempy<=maxn

2)在计算结果的时候,是需要这样减去的ask(c,d)-ask(a-1,d)-ask(c,b-1)+ask(a-1,b-1)

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn=5004; 
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int  mp[maxn][maxn];
int n,m;
//!!!!为什么最后一个点过不了!!!! 
void add(int x,int y,int xx){
	int y1;
	while(x<=maxn){
		y1=y;
		while(y1<=maxn){
			mp[x][y1]+=xx;
			y1+=lowbit(y1);
		}
		x+=lowbit(x);
	}
}
LL ask(int x,int y){
	LL ans=0;
	while(x){
		int temp=y;
		while(temp){
			ans+=mp[x][temp];
			temp-=lowbit(temp);
		}
		x-=lowbit(x);
	}
	return ans;
}
int main(){
	scanf("%d %d",&n,&m);
	int op,xx,c,d,a,b;
	while(scanf("%d",&op)!=EOF){
		
		if(op==1){
			scanf("%d %d %d",&a,&b,&xx);
			add(a,b,xx);
		}
		else if(op==2){
			scanf("%d %d %d %d",&a,&b,&c,&d);
			
			printf("%lld\n",ask(c,d)-ask(a-1,d)-ask(c,b-1)+ask(a-1,b-1));
			//这样减的!!!!! 
		}
	}
return 0;
}

  

 

 posted on 2020-04-19 17:57  shirlybabyyy  阅读(446)  评论(0编辑  收藏  举报