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; }