• 水比的 树状数组 刷题记录


    洛谷P3374 【模板】树状数组 1

    • 单点修改,区间查值。细节看代码
    • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 500010
     3 
     4 using namespace std;
     5 int n,m;
     6 int a[nmax],qz[nmax]={0},c[nmax];
     7 
     8 inline int lowb(int x){ return x&(-x); }
     9 
    10 void build(){
    11     cin>>n>>m;
    12     for (int i=1; i<=n; i++) {
    13         scanf("%d",&a[i]);
    14         qz[i]=qz[i-1]+a[i];
    15         c[i]=qz[i]-qz[i-lowb(i)];
    16     }
    17 }
    18 
    19 inline void add(int x,int k){
    20     a[x]+=k;      //这里x+=lowb(x)是走向它父亲节点所以先加后走,判断时是x<=n
    21     while(x<=n) {  c[x]+=k; x+=lowb(x); }
    22 }
    23 
    24 inline int findqz(int x){  //求1~x的和
    25     int ans=0;
    26     while(x) { ans+=c[x]; x-=lowb(x);   }
    27     return ans;
    28 }
    29 
    30 inline int query(int x,int y) { return findqz(y)-findqz(x-1); }
    31 
    32 int main(){
    33     build();
    34     int b,x,y;
    35     for (int i=0; i<m; i++) {
    36         scanf("%d%d%d",&b,&x,&y);
    37         if(b==1)  add(x,y);
    38         else printf("%d
    ",query(x,y));
    39     }
    40     return 0;
    41 }
    • 写树状数组的感觉比写线段树的感觉好多了~在家里一个人打线段树好无聊,都没有朋友玩,没有女仔玩。打了树状数组发现个个都是位运算,行数又少,超喜欢树状数组的。

    洛谷P3368 【模板】树状数组 2

    • 区间修改(区间加),单点查值。
    • 代码:
       1 #include <bits/stdc++.h>
       2 #define nmax 500010
       3 
       4 using namespace std;
       5 int n,m;
       6 int a[nmax],c[nmax]={0};
       7 
       8 inline int lowb(int x){ return x&(-x); }
       9 
      10 inline void ta(int x,int k){  //把1~x的c[i]都加上k
      11     while(x) { c[x]+=k; x-=lowb(x);   }
      12 }
      13 
      14 inline void add(int x,int y,int k){
      15     ta(x-1,-k);
      16     ta(y,k);
      17 }
      18 
      19 inline int query(int x) {
      20     int tot=a[x];  //得在x还没有变的时候把a[x]给加上
      21     while(x<=n) {  tot+=c[x]; x+=lowb(x); }
      22     return tot;
      23 }
      24 
      25 int main(){
      26     cin>>n>>m;
      27     for (int i=1; i<=n; i++) scanf("%d",&a[i]);
      28     int b,x,y,k;
      29     for (int i=0; i<m; i++) {
      30         scanf("%d",&b);
      31         if(b==1) {
      32                 scanf("%d%d%d",&x,&y,&k);
      33                 add(x,y,k);
      34         }
      35         else { scanf("%d",&x); printf("%d
      ",query(x)); }
      36     }
      37     return 0;
      38 }
      View Code

     BZOJ 1106: [POI2007]立方体大作战tet

    • 思路:拿例子 5 2 3 1 4 1 4 3 5 2来说

                         看某一段例如   5 2 3 1 4 1 4 3 5这段,我肯定不先消去55,因为55中间有11,44,33没消,如果划去55,消去11,44,33的代价不变,但是划去11,44,33的话,消去55的代价就变低了,所以对于连个相同的数中间还有相同的数的情况,先把中间相同的数删去。

                        剩下的一串数就是中间没有相同的数了,拿样例二来说1 2 3 1 2 3,考虑这样一种策略,我第二次走到这个数时,我移动并删去它。

                        走到i=4了,删去11,移动两次,数组变成 2 3 2 3,继续向后走到2,变成 3 3,最后删去 3 3.

                        也就是说,光看策略的话,是一直删去的那些中间没有相同的数的数,直到删完,拿例子一来说:

                       开始5 2 3 1 4 1 4 3 5 2

                              5 2 3 4 4 3 5 2

                              5 2 3 3 5 2

                              5 2 5 2

                              2 2         结束

    • 怎么用树状数组维护?

                       不看第二次出现的数,只统计区间内第一次出现的数的个数,然后挨个删去并统计代价即可。

                       单点修改,区间查值。

    • 代码:
     1 #include <bits/stdc++.h>
     2 #define nmax 50010
     3 #define lowbit(x) (x&(-x))
     4 //单点修改,区间查值
     5 using namespace std;
     6 int n;
     7 int a[nmax*2],id[nmax*2]={0},c[nmax*2]={0};//id -> 记录第一次出现的坐标
     8 //c -> 区间内不同的数的个数
     9 inline void add(int j,int k){
    10     while(j<=n) { c[j]+=k; j+=lowbit(j); }
    11 }
    12 
    13 void build(){
    14     cin>>n;
    15     n*=2;
    16     for (int i=1; i<=n; i++) {
    17         scanf("%d",&a[i]);
    18         int& t=id[a[i]];
    19         if( !t ) {  t=i; add(i,1); } //第一次出现
    20     }
    21 }
    22 
    23 inline int tf(int x) {
    24     int ans=0;
    25     while(x>=1){ ans+=c[x]; x-=lowbit(x); }
    26     return ans;
    27 }
    28 //找x~y中间不同且第一次出现的数
    29 int myfind(int x,int y){ return tf(y-1)-tf(x); }
    30 
    31 int main(){
    32     build();
    33     int ans=0;
    34     for (int i=1; i<=n; i++) {
    35         int t=id[a[i]];
    36         if(t!=i) {
    37             ans+=myfind(t,i);
    38             add(t,-1);
    39         }
    40     }
    41     cout<<ans<<endl;
    42     return 0;
    43 }
    点人家看代码030

     BZOJ3155: Preprefix sum

    • 题意:单点修改,然后区间查询前缀和的前缀和
    • 从右面看SS2就等于四个一和三个2减去2倍的S2(S是前缀和),然后考虑维护两个树状数组
    • 代码(注意loong long)
       1 #include <bits/stdc++.h>
       2 #define nmax 100010
       3 #define lowbit(x) (x&(-x))
       4  
       5 using namespace std;
       6 typedef long long ll;
       7 ll a[nmax][3]={0},c[nmax][3]={0},qz[nmax][3]={0};//两个单点修改,区间查值
       8 int n,m;
       9  
      10 void build(){
      11     scanf("%d%d",&n,&m);
      12     for (int i=1; i<=n; i++) {
      13         scanf("%lld",&a[i][1]);//1是前缀和,2是前缀和的前缀和
      14         a[i][2]=a[i][1]*(n-i+1);
      15         qz[i][1]=a[i][1]+qz[i-1][1];
      16         qz[i][2]=a[i][2]+qz[i-1][2];
      17         c[i][1]=qz[i][1]-qz[i-lowbit(i)][1];
      18         c[i][2]=qz[i][2]-qz[i-lowbit(i)][2];
      19     }
      20 }
      21  
      22 inline void add(int x,ll k,int id){ //给x加上k
      23     while(x<=n){ c[x][id]+=k; x+=lowbit(x); }
      24 }
      25  
      26 inline ll query(int x){  //SSx的值
      27     ll ans=0,ta=0;
      28     int k=x;//到后面已经改变了,到底多少次才会记住拉
      29     while(x>0) {
      30         ans+=c[x][2];
      31         ta+=c[x][1];
      32         x-=lowbit(x);
      33     }
      34     ans-=(ta*(n-k));
      35     return ans;
      36 }
      37  
      38 int main(){
      39     build();
      40     char in[20];
      41     int x,y;
      42     while(m--){
      43         scanf("%s",in);
      44         if(in[0]=='Q'){
      45             scanf("%d",&x);
      46             printf("%lld
      ",query(x));
      47         }else{
      48             scanf("%d%d",&x,&y);
      49             ll ta=y-a[x][1];
      50             a[x][1]=y;
      51             add(x,ta,1);
      52             add(x,ta*(n-x+1),2);
      53         }
      54     }
      55     return 0;
      56 }
      φ(≧ω≦*)♪

    HDU1541 Stars

    • 二维偏序,用的树状数组
    • xy已经排好序所以只看x,y[i-1]~y[1]的一定是小于y[i]的,只要统计x[i-1]~x[1]中小于x[i]的个数就行
    • 用结构体排序,c[i]表示当前x[1]~x[i]的个数,每次找到排序后最大的辣个x的下标id,查找c[id],然后删除x[id]
    • 区间查值,单点修改
    • 代码:(坑点众多,一定要看discuss  quq)
       1 #include <bits/stdc++.h>
       2 #define nmax 15010
       3 #define lowbit(x) (x&(-x))
       4 
       5 using namespace std;
       6 typedef long long ll;
       7 int n,y;
       8 int x[nmax],c[nmax],an[nmax]={0};
       9 struct point{
      10     int x,id;
      11     bool operator < (const point a) const{
      12         return (a.x==x)?(a.id<id):(a.x<x);
      13     }
      14 }p[nmax];
      15 
      16 inline int query(int x){
      17     int ans=0;
      18     while(x>0) { ans+=c[x]; x-=lowbit(x); }
      19     return ans;
      20 }
      21 
      22 inline void del(int x){
      23     while(x<=n){ c[x]--; x+=lowbit(x); }
      24 }
      25 
      26 int main(){
      27     while(scanf("%d",&n)!=EOF){
      28         memset(an,0,sizeof(an));
      29         for (int i=1; i<=n; i++) {
      30                 scanf("%d%d",&x[i],&y);
      31                 p[i].x=x[i];
      32                 p[i].id=i;
      33                 c[i]=lowbit(i);
      34         }
      35         sort(p+1,p+1+n);
      36         for (int i=1; i<=n; i++) {
      37             int t=query(p[i].id)-1;
      38             an[t]++;
      39             del(p[i].id);
      40         }
      41         for (int i=0; i<n; i++) printf("%d
      ",an[i]);
      42     }
      43     return 0;
      44 }
      (;′⌒`)

    HDU 1394Minimum Inversion Number

    • 一定要把题意看清了再做题!!!
    • hud上做题一定要多组数据输入输出!!!
    • 考虑最前面的那个数,它后面大于和小于它的数已知,则把它移到最后所增加的逆序对数可以O(1)求出
    • 代码:while(scanf("%d",&n))会tle。。。一定要加EOF
       1 #include <bits/stdc++.h>
       2 #define nmax 5010
       3 #define lowbit(x) x&(-x)
       4 #define inf 1e9
       5 
       6 using namespace std;
       7 int n;
       8 int a[nmax],c[nmax]={0};
       9 
      10 void madd(int p){
      11     while(p<=n){ c[p]++; p+=lowbit(p); }
      12 }
      13 
      14 int mfind(int p){
      15     int ans=0;
      16     while(p>0) { ans+=c[p]; p-=lowbit(p); }
      17     return ans;
      18 }
      19 
      20 int main(){
      21     //freopen("owo.txt","r",stdin);
      22     while(scanf("%d",&n)!=EOF){
      23         for (int i=1; i<=n; i++) {
      24             scanf("%d",&a[i]);
      25             a[i]++;
      26             c[i]=0;    
      27         }
      28         int ta=0,t,tp,ans=inf;
      29         //找逆序对
      30         for (int i=1; i<=n; i++){
      31             t=mfind(a[i]);
      32             ta+=(i-t-1);
      33             madd(a[i]);
      34         }
      35         for (int i=1; i<=n; i++) {
      36             ta+=(n-a[i]);
      37             ta-=(a[i]-1);
      38             ans=min(ans,ta);
      39         }
      40         cout<<ans<<endl;
      41     }
      42     return 0;
      43 }
      (╯‵□′)╯︵┻━┻
  • 相关阅读:
    JAVA小记 (1)
    页面布局
    布局
    JDBC连接数据库
    国际化项目
    Class打包成jar
    Description Resource Path Location Type Java compiler level does not match the version of(编译问题)
    SSM框架下 Failed to load resource: the server responded with a status of 404 (Not Found)错误
    DES加密算法
    HashMap 和 Hashtable 的 6 个区别,一般人不知道最后一条
  • 原文地址:https://www.cnblogs.com/jiecaoer/p/11279854.html
Copyright © 2020-2023  润新知