• 树状数组及其拓展


    作用:

     

     查询应该是res+=tr[i]

    目录

    1.楼兰图腾

    2.一个简单的整数问题(差分)

    3.一个简单的整数问题2(差分+分析)

    4.谜一样的牛(二分查找)


    1.楼兰图腾

    tr表示这个位置有多少个这个数

    241. 楼兰图腾 - AcWing题库

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=2e5+10;
    5. int n;
    6. int tr[N],a[N];
    7. ll up[N],down[N];//up记录上升的有多少个数,down记录下降的有多少个数
    8. int lowbit(int x)//lowbit运算
    9. {
    10. return x&-x;
    11. }
    12. void add(int x,int c)//某个位置加一个数的操作
    13. {
    14. for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
    15. }
    16. int sum(int x)//算前x个的和
    17. {
    18. int res=0;
    19. for(int i=x;i;i-=lowbit(i)) res+=tr[i];
    20. return res;
    21. }
    22. int main()
    23. {
    24. scanf("%d",&n);
    25. for(int i=0;iscanf("%d",&a[i]);
    26. //一开始tr是0,啥也没有一个数都没有
    27. for(int i=0;i
    28. {
    29. int y=a[i];
    30. up[i]=sum(n)-sum(y);//表示目前为止,0~i位置有多少个数是大于大于y的
    31. down[i]=sum(y-1);//表示目前为止,0~i位置有多少个数是小于y的
    32. add(y,1);//该位置添加个y
    33. }
    34. ll res1=0,res2=0;
    35. //现在的tr是已经弄到了整个序列的数了
    36. for(int i=0;i
    37. {
    38. int y=a[i];
    39. res1+=up[i]*(ll)(sum(n)-sum(y));//答案就是左边大于y的数乘以右边大于y的数
    40. res2+=down[i]*sum(y-1);//答案就是左边小于y的数乘以右边小于y的数
    41. add(y,-1);//把这个数去掉,不然待会算右边会算到这个数,实际是这个数已经用过了
    42. }
    43. printf("%lld %lld\n",res1,res2);
    44. return 0;
    45. }

    2.一个简单的整数问题(差分

    242. 一个简单的整数问题 - AcWing题库

    把tr数组当成差分数组即可

    假如[l,r]+c,则b[l]+=c,b[r+1]-=c;

    假如求x的数是多少,相当于求b[x]的前缀和,然后可以直接用树状数组求用sum(x)即可

    1. #include
    2. using namespace std;
    3. const int N=1e5+10;
    4. int n,m;
    5. int tr[N],a[N];//tr就是差分数列
    6. int lowbit(int x)//lowbit运算
    7. {
    8. return x&-x;
    9. }
    10. void add(int x,int c)//某个位置加一个数的操作
    11. {
    12. for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
    13. }
    14. int sum(int x)//求前x项和
    15. {
    16. int res=0;
    17. for(int i=x;i;i-=lowbit(i)) res+=tr[i];
    18. return res;
    19. }
    20. int main()
    21. {
    22. scanf("%d%d",&n,&m);
    23. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    24. for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);//让tr是a[i]的差分,则b[i]=a[i]-a[i-1]
    25. while(m--)
    26. {
    27. char str[2];
    28. scanf("%s",str);
    29. if(str[0]=='Q')
    30. {
    31. int op;
    32. scanf("%d",&op);
    33. printf("%d\n",sum(op));//输出前op项和
    34. }
    35. else
    36. {
    37. int l,r,c;
    38. scanf("%d%d%d",&l,&r,&c);
    39. add(l,c),add(r+1,-c);//差分,b[l]+=c,b[r+1]-=c
    40. }
    41. }
    42. return 0;
    43. }

    3.一个简单的整数问题2(差分+分析)

    243. 一个简单的整数问题2 - AcWing题库

    弄两个树状数组,其中一个是差分数组b[i],另一个是i*b[i]

     

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. const int N=1e5+10;
    5. int n,m;
    6. int a[N];
    7. ll tr1[N],tr2[N];//tr1记录的是b[i],tr2是i*b[i]
    8. int lowbit(int x)//lowbit运算
    9. {
    10. return x&-x;
    11. }
    12. void add(ll tr[],int x,ll c)//某个树状数组的加数
    13. {
    14. for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
    15. }
    16. ll sum(ll tr[],int x)//某个树状数组的求前缀和
    17. {
    18. ll res=0;
    19. for(int i=x;i;i-=lowbit(i)) res+=tr[i];
    20. return res;
    21. }
    22. ll ans(int x)//求总数组的前x项的和
    23. {
    24. return sum(tr1,x)*(x+1)-sum(tr2,x);
    25. }
    26. int main()
    27. {
    28. scanf("%d%d",&n,&m);
    29. for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    30. 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]));//初始化初始值
    31. while(m--)
    32. {
    33. char op[2];
    34. int l,r,c;
    35. scanf("%s%d%d",op,&l,&r);
    36. if(op[0]=='Q') printf("%lld\n",ans(r)-ans(l-1));//求l~r的和,也就是1~r的和减去1~l的和
    37. else
    38. {
    39. scanf("%d",&c);
    40. //b[l]+=c
    41. add(tr1,l,c),add(tr2,l,l*c);
    42. //b[r+1]-=c;
    43. add(tr1,r+1,-c),add(tr2,r+1,(ll)-c*(r+1));
    44. }
    45. }
    46. return 0;
    47. }

    4.谜一样的牛(二分查找

    244. 谜一样的牛 - AcWing题库

    思路:从n往1看,每次给剩余的数里面第a[i]+1小的数,然后把这个数删掉,一直这样操作即可

    找第a[i]+1小的数:

    1.把每个数初始值为1,表明这个数还没用过,每个前缀和表明了当前数是第几小的数

    2.二分查找第a[i]+1小的数,因为每个数的前缀和是递增的可以二分

    1. #include
    2. using namespace std;
    3. const int N=1e5+10;
    4. int n;
    5. int a[N],tr[N],ans[N];
    6. int lowbit(int x)
    7. {
    8. return x&-x;
    9. }
    10. void add(int x,int c)
    11. {
    12. for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
    13. }
    14. int sum(int x)
    15. {
    16. int res=0;
    17. for(int i=x;i;i-=lowbit(i)) res+=tr[i];
    18. return res;
    19. }
    20. int main()
    21. {
    22. scanf("%d",&n);
    23. for(int i=2;i<=n;i++) scanf("%d",&a[i]);
    24. for(int i=1;i<=n;i++) add(i,1);//初始化初始值,让每个数都为1,表明没有用过
    25. for(int i=n;i;i--)
    26. {
    27. int b=a[i]+1;//找第a[i]+1小的数
    28. int l=1,r=n;
    29. while(l//二分
    30. {
    31. int mid=l+r>>1;
    32. if(sum(mid)>=b) r=mid;//假如前缀和大了,说明当前数不是第b小的数
    33. else l=mid+1;
    34. }
    35. ans[i]=l;//让答案等于当前找到的第b小的数
    36. add(l,-1);//把当前数删除,因为已经用过了
    37. }
    38. for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    39. return 0;
    40. }

  • 相关阅读:
    变量名命名的艺术
    【蜂鸟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