作用:




查询应该是res+=tr[i]
目录
tr表示这个位置有多少个这个数
- #include
- using namespace std;
- typedef long long ll;
- const int N=2e5+10;
- int n;
- int tr[N],a[N];
- ll up[N],down[N];//up记录上升的有多少个数,down记录下降的有多少个数
- int lowbit(int x)//lowbit运算
- {
- return x&-x;
- }
- void add(int x,int c)//某个位置加一个数的操作
- {
- for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
- }
- int sum(int x)//算前x个的和
- {
- int res=0;
- for(int i=x;i;i-=lowbit(i)) res+=tr[i];
- return res;
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=0;i
scanf("%d",&a[i]); - //一开始tr是0,啥也没有一个数都没有
- for(int i=0;i
- {
- int y=a[i];
- up[i]=sum(n)-sum(y);//表示目前为止,0~i位置有多少个数是大于大于y的
- down[i]=sum(y-1);//表示目前为止,0~i位置有多少个数是小于y的
- add(y,1);//该位置添加个y
- }
- ll res1=0,res2=0;
- //现在的tr是已经弄到了整个序列的数了
- for(int i=0;i
- {
- int y=a[i];
- res1+=up[i]*(ll)(sum(n)-sum(y));//答案就是左边大于y的数乘以右边大于y的数
- res2+=down[i]*sum(y-1);//答案就是左边小于y的数乘以右边小于y的数
- add(y,-1);//把这个数去掉,不然待会算右边会算到这个数,实际是这个数已经用过了
- }
- printf("%lld %lld\n",res1,res2);
- return 0;
- }
2.一个简单的整数问题(差分)
把tr数组当成差分数组即可
假如[l,r]+c,则b[l]+=c,b[r+1]-=c;
假如求x的数是多少,相当于求b[x]的前缀和,然后可以直接用树状数组求用sum(x)即可
- #include
- using namespace std;
- const int N=1e5+10;
- int n,m;
- int tr[N],a[N];//tr就是差分数列
- int lowbit(int x)//lowbit运算
- {
- return x&-x;
- }
- void add(int x,int c)//某个位置加一个数的操作
- {
- for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
- }
- int sum(int x)//求前x项和
- {
- int res=0;
- for(int i=x;i;i-=lowbit(i)) res+=tr[i];
- return res;
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- for(int i=1;i<=n;i++) scanf("%d",&a[i]);
- for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);//让tr是a[i]的差分,则b[i]=a[i]-a[i-1]
- while(m--)
- {
- char str[2];
- scanf("%s",str);
- if(str[0]=='Q')
- {
- int op;
- scanf("%d",&op);
- printf("%d\n",sum(op));//输出前op项和
- }
- else
- {
- int l,r,c;
- scanf("%d%d%d",&l,&r,&c);
- add(l,c),add(r+1,-c);//差分,b[l]+=c,b[r+1]-=c
- }
- }
- return 0;
- }
3.一个简单的整数问题2(差分+分析)
弄两个树状数组,其中一个是差分数组b[i],另一个是i*b[i]

- #include
- using namespace std;
- typedef long long ll;
- const int N=1e5+10;
- int n,m;
- int a[N];
- ll tr1[N],tr2[N];//tr1记录的是b[i],tr2是i*b[i]
- int lowbit(int x)//lowbit运算
- {
- return x&-x;
- }
- void add(ll tr[],int x,ll c)//某个树状数组的加数
- {
- for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
- }
- ll sum(ll tr[],int x)//某个树状数组的求前缀和
- {
- ll res=0;
- for(int i=x;i;i-=lowbit(i)) res+=tr[i];
- return res;
- }
- ll ans(int x)//求总数组的前x项的和
- {
- return sum(tr1,x)*(x+1)-sum(tr2,x);
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- for(int i=1;i<=n;i++) scanf("%d",&a[i]);
- for(int i=1;i<=n;i++) add(tr1,i,a[i]-a[i-1]),add(tr2,i,(ll)i*(a[i]-a[i-1]));//初始化初始值
- while(m--)
- {
- char op[2];
- int l,r,c;
- scanf("%s%d%d",op,&l,&r);
- if(op[0]=='Q') printf("%lld\n",ans(r)-ans(l-1));//求l~r的和,也就是1~r的和减去1~l的和
- else
- {
- scanf("%d",&c);
- //b[l]+=c
- add(tr1,l,c),add(tr2,l,l*c);
- //b[r+1]-=c;
- add(tr1,r+1,-c),add(tr2,r+1,(ll)-c*(r+1));
- }
- }
- return 0;
- }
4.谜一样的牛(二分查找)
思路:从n往1看,每次给剩余的数里面第a[i]+1小的数,然后把这个数删掉,一直这样操作即可
找第a[i]+1小的数:
1.把每个数初始值为1,表明这个数还没用过,每个前缀和表明了当前数是第几小的数
2.二分查找第a[i]+1小的数,因为每个数的前缀和是递增的可以二分
- #include
- using namespace std;
- const int N=1e5+10;
- int n;
- int a[N],tr[N],ans[N];
- int lowbit(int x)
- {
- return x&-x;
- }
- void add(int x,int c)
- {
- for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
- }
- int sum(int x)
- {
- int res=0;
- for(int i=x;i;i-=lowbit(i)) res+=tr[i];
- return res;
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=2;i<=n;i++) scanf("%d",&a[i]);
- for(int i=1;i<=n;i++) add(i,1);//初始化初始值,让每个数都为1,表明没有用过
- for(int i=n;i;i--)
- {
- int b=a[i]+1;//找第a[i]+1小的数
- int l=1,r=n;
- while(l
//二分 - {
- int mid=l+r>>1;
- if(sum(mid)>=b) r=mid;//假如前缀和大了,说明当前数不是第b小的数
- else l=mid+1;
- }
- ans[i]=l;//让答案等于当前找到的第b小的数
- add(l,-1);//把当前数删除,因为已经用过了
- }
- for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
- return 0;
- }
-
相关阅读:
变量名命名的艺术
【蜂鸟E203的FPGA验证】Chap.9 FPGA平台硬件验证
行内元素文字背景被截断的问题,如何进行修改?
树模型(三)决策树
C语言基于AVL树实现简单的文件数据库
Python 在windows环境下加密文件成.pyd格式
【ROS进阶篇】第一讲 常用API介绍
【2022秋招面经】——NLP
大前端CPU优化技术--NEON指令介绍
思修复习知识点-《思想道德基础与法律修养》
-
原文地址:https://blog.csdn.net/m0_63729880/article/details/126751728