树状数组入门练习

Powered by:AB_IN 局外人

介绍

  • 首先,#define lowbit(x) ((x) & -(x)) 功能是找到x的二进制数的最后一个1,也就是查询前驱和后继
    例如lowbit(6)=2
    • t r e e [ 6 ] = a 5 + a 6 tree[6]=a_5+a_6 tree[6]=a5+a6
    • 6的前驱是4(6-2),后继是8(6+2)。
    • 前驱:个人理解是不包括tree[x]前面的一个点。比如6的前驱是4, t r e e [ 6 ] = a 5 + a 6 tree[6]=a_5+a_6 tree[6]=a5+a6 t r e e [ 4 ] = a 1 + a 2 + a 3 + a 4 tree[4]=a_1+a_2+a_3+a_4 tree[4]=a1+a2+a3+a4。再继续查询前驱,发现lowbit(4)=4,那么4没有前驱,只有后继(6)。
  • 两个函数 单点更新和区间查询
typedef long long ll;
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
inline void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);//查询x的后继们
    }
}
ll sum(ll x)//前缀和
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);//查询x的前驱们
    }
    return sum;
}

P3374 【模板】树状数组 1

单点更新和区间查询
这里区间查询就是求区间和。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
int n,m,tree[2000010],t,a,b,flag;

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

int main()
{
    cin>>n>>m;
    rep(i,1,n){
        cin>>t;
        add(i,t);
    }
    rep(i,1,m){
        cin>>flag>>a>>b;
        if(flag==1) add(a,b);
        if(flag==2) cout<<sum(b)-sum(a-1)<<endl;
    }
    return 0;
}

P3368 【模板】树状数组 2

区间更新和单点查询
涉及到差分的知识,在菜鸡之前博客有提到。
这里的单点查询是指查询这个点的值。
这个题的思路就是将原数组的差分数组放入树状数组中去。
一个点的值就是原数组那一点的值。(前缀和和差分抵消了)

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
int n,m,tree[2000010],t,a,b,flag,tmp,d;

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

int main()
{
    cin>>n>>m;
    rep(i,1,n){
        cin>>t;
        add(i,t-tmp);
        tmp=t;
    }
    rep(i,1,m){
        cin>>flag;
        if(flag==1){
            cin>>a>>b>>d;
            add(a,d);
            add(b+1,-d);
        }

        if(flag==2) {cin>>a;cout<<sum(a)<<endl;}
    }
    return 0;
}

P2068 统计和

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
int n,w,tree[2000010],x,y;
char flag;

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    cin>>n>>w;
    rep(i,1,w){
        cin>>flag>>x>>y;
        if(flag=='x') add(x,y);
        else cout<<sum(y)-sum(x-1)<<endl;
    }
    return 0;
}

P1428 小鱼比可爱

普通做法。

#include<iostream>
using namespace std;
int a[105],b[105],n;
int main()
{
    cin>>n;
    for (int i=1;i<=n;i++) cin>>a[i];
    for (int i=1;i<=n;i++)
        for (int j=i;j>=1;j--){
            if (a[j]<a[i])  b[i]++;
        }
    for (int i=1;i<=n;i++) cout<<b[i]<<" ";
    return 0;
}

树状数组
去重离散化再用树状数组
去重离散化就是让每个数变成这一些数中第几小的数。
比如 1   5   8   9   3   2 1 \ 5\ 8\ 9\ 3\ 2 1 5 8 9 3 2
变成 1   4   5   6   3   2 1\ 4 \ 5 \ 6 \ 3 \ 2 1 4 5 6 3 2
在这里插入图片描述

然后把这一串离散化过的放入树状数组中。

  • add(a[i],1)的意思就是标记这个点已经计数了,让后驱也记上数。
  • b[i]=sum(a[i]-1)的意思得好好想一想。
    • 这个题让求的是比这个数小的左边的数有几个。

    • 我们先举例子说一下:先让1进去那么树状数组就会变成在这里插入图片描述
      他左边是一定没有数的,所以sum(a[1]-1)=0

      然后放第二个数4。
      在这里插入图片描述
      这下a[2]-1=3,sum(3)=1。差不多就可以看出规律了。
      因为4是第二个进入的,那么如果sum(a[4])=2,就代表现在所有的数都在4的左侧,即都小于等于a[4]。如果sum(a[4])<2,说明了有数在4的左边,那么就有比a[4]大的数。

所以规律如下:
[比我小] : s u m ( a [ i ] − 1 ) sum(a[i]-1) sum(a[i]1)
[小于等于我] : s u m ( a [ i ] ) sum(a[i]) sum(a[i])
[比我大] : i − s u m ( a [ i ] ) i-sum(a[i]) isum(a[i]) (逆序对)
[大于等于我] : i − s u m ( a [ i ] − 1 ) i-sum(a[i]-1) isum(a[i]1)

下标是离散化后的数!!!!!

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],t[maxn],tree[maxn],b[maxn];
void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

long long ans;


int main()
{
    cin>>n;
    rep(i,1,n){
        cin>>a[i];
        t[i]=a[i];
    }
    sort(t+1,t+1+n);
    int m=unique(t+1,t+1+n)-t-1;
    rep(i,1,n){
        a[i]=lower_bound(t+1,t+1+m,a[i])-t;
    }
    rep(i,1,n){
        add(a[i],1);
        b[i]=sum(a[i]-1);
    }
    rep(i,1,n) cout<<b[i]<<" ";
    return 0;
}

P2344 [USACO11FEB] Generic Cow Protests G

这个题实在是不好想。
首先肯定先想到dp,设 t r e e [ i ] tree[i] tree[i]为在第i位时的方案数
t r e e [ i ] = ∑ t r e e [ j ] ( j < i , a [ i ] ≥ a [ j ] ) tree[i]=∑tree[j](j<i,a[i]≥a[j]) tree[i]=tree[j](j<i,a[i]a[j])
这里 t r e e tree tree是dp数组, a a a为前缀和数组。

ps:为什么这么写呢?
t r e e [ i ] tree[i] tree[i]就是 i i i之前可以怎么分组,顺着递推下去就行了。

然后看出是求在 i i i前面的并且前缀和要小于等于 a [ i ] a[i] a[i] t r e e [ j ] tree[j] tree[j]的和。
树状数组 维护下标为a[i]值为方案数 tree[i]的前缀和。

这里真的要好好缕一缕

  • 首先,怎么做到判断前缀和比 a [ i ] a[i] a[i]小的?
    这是靠 a [ i ] a[i] a[i]作为下标来进行的。 a [ i ] a[i] a[i]先输进去作为下标,加和前面的下标的值,就行了。而且关键是,这还是按顺序输入的下标(即 a [ i ] a[i] a[i])。
  • 其次,怎么维护方案数的?
    可以想一下小鱼比可爱的题我们是怎么看一个数,前面比他小的数有多少的?就是通过 a [ i ] a[i] a[i]作为下标,然后下标对应的值 + 1 +1 +1,这里的值因为这代表有这一个数。那么把这里的数换成方案数,不就行了。

还要注意的是数据有负数,所以需要离散化。

#include <bits/stdc++.h>
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5, mod = 1e9 + 9;
int n, a[maxn], b[maxn], tree[maxn];
inline void add(ll x,ll d)
{
    while(x<=n){
        tree[x]=(tree[x]+d)%mod;
        x+=lowbit(x);//查询x的后继们
    }
}
ll sum(ll x)//前缀和
{
    ll sum=0;
    while(x>0){
        sum=(sum+tree[x])%mod;
        x-=lowbit(x);//查询x的前驱们
    }
    return sum;
}
int main () {
    cin >> n;
    rep(i,1,n){
        cin >> a[i];
        a[i] += a[i-1];
        b[i] = a[i];
    }
    sort(b + 1, b + n + 1);
    rep(i,0,n)//注意这里是从0开始离散化,目的就是让sum[0]的地方变成最小的标号。
        a[i] = lower_bound(b+1, b+n+1, a[i]) - b ;//这个地方,如果是减b,那么最小下标从1开始,如果减b加1,那么最小下标从2开始。
    ll ans = 0;
    add(a[0],1);//先把基准加进去。(计数dp初始化)
    rep(i,1,n) {
        ans = sum(a[i]);//在逆序对那说过,这就是求小于等于我的数,其实就是sum(a[i])-sum(a[i之前下标])>=0的数量
        add(a[i], ans);//加上方案数
    }
    cout << ans << endl;
    return 0;
}

P1972 [SDOI2009]HH的项链

其实懂了题目意思之后,这就是个板子题。
注意几个点吧:

  • 数据很大,不用快读可能会卡三个T。
  • 需要根据查询数据的r进行升序排序,根据r的大小依次进行操作。
  • 排完序了,别忘了按原来的输入顺序输出结果。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
const int maxn=1e6+10;
int vis[maxn];
ll tree[maxn],a[maxn],id[maxn],ans[maxn];
ll n,m,l,r;
inline void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
struct sa{
    int l;
    int r;
    int pos;
}q[maxn];

bool cmp(const struct sa &a,const struct sa &b)
{
    return a.r<b.r;
}

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline ll read(){
        register ll x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


int main()
{
    n=read();
    rep(i,1,n){
        a[i]=read();
    }
    m=read();
    rep(i,1,m){
        q[i].l=read();q[i].r=read();
        q[i].pos=i;
    }
    sort(q+1,q+1+m,cmp);
    int tmp=1;//最左
    rep(i,1,m){
        rep(j,tmp,q[i].r){//一开始,从左端点 -> r最小的第一组数据
            if(vis[a[j]])//如果数据存在
                add(id[a[j]],-1);//就在之前那个数据的下标那消去,id数组存的是下标
            add(j,1);
            id[a[j]]=j;//更新下标
            vis[a[j]]=1;
        }
        tmp=q[i].r+1;//更新左端点
        ans[q[i].pos]= sum(q[i].r)-sum(q[i].l-1);
    }
    rep(i,1,m){
        write(ans[i]);
        pc('\n');
    }
    return 0;
}

P1966 火柴排队

  • 思路挺简单的,题意就是要最后的结果结果最小嘛,那么两个数列第k小的数应该一一对应才对。
    那么很显然要先离散化,但是这题的坑点也就恰好在离散化。这个等会再说。
    离散化之后,本题的精华就在于新建数列 c c c ,另 c [ a [ i ] ] = b [ i ] c[a[i]]=b[i] c[a[i]]=b[i]。就是说以 a [ i ] a[i] a[i]作为关键字去对 b [ i ] b[i] b[i]进行排序,那么如果是一一对应的,那么 c c c应该是升序的。
    那么将原本乱的 c c c 序列升序排列的最少交换次数。这不就是归并排序的思路吗?自然而然就是求逆序对

  • 好了,开始回过头来写离散化,手写的去重离散化只得了10分,这就让我很吃惊……打开了评论区,发现很多都有这个问题,而且问题就在于和我用的一样的方法离散化。为了弄清本质问题,菜鸡debug了一下午……

如果您也是一样的问题,请耐心往下看:

10分的可能和本菜鸡一样用的lower_bound搭配unique的离散化。但是这样的话,两个相同的元素,它们的大小顺序是相同的

比如我们离散化这样一组数据 3 3 5 6 8 -> 1 1 2 3 4

而我们实际需要的是 1 2 3 4 5。

其实wa9个点,有可能不是因为wa,而是因为tle了 (没看过数据,瞎猜的)

为什么呢?众所周知,这题的精华就在于新建数列, c [ a [ i ] ] = b [ i ] c[a[i] ]=b[i] c[a[i]]=b[i] 但 比如 用lower_bound+unique离散化后的数据为

a :1 1 2 3 4

b: 2 2 3 4 5

这样的话c数组是没有下标5的!!!这样在循环中 i = 5 i=5 i=5时就会绕进去,导致TLE。

所以可以选择用结构体离散化的方式,题解里基本都是这样的。

结构体离散化(序号无重复)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
const int mod=1e8-3;
const int maxn=1e5+10;
ll n,tree[maxn],c[maxn];
ll ans;

struct sa{
    ll pos;
    ll val;
}a[maxn],b[maxn];

inline void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        tree[x]%=mod;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        sum%=mod;
        x-=lowbit(x);
    }
    return sum;
}

bool cmp(const struct sa &a,const struct sa &b)
{
    if(a.val!=b.val) return a.val<b.val;
    else return a.pos<b.pos;
}

int main()
{
    cin>>n;
    rep(i,1,n){
        cin>>a[i].val;
        a[i].pos=i;
    }
    rep(i,1,n){
        cin>>b[i].val;
        b[i].pos=i;
    }
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    rep(i,1,n){
        c[a[i].pos]=b[i].pos;
    }
    rep(i,1,n){
        add(c[i],1);
        ans=(ans+i-sum(c[i]))%mod;
    }
    cout<<ans<<endl;
    return 0;
}

lower_bound+unique(序号有重复)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) ((x) &-(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
const int mod=1e8-3;
const int maxn=1e5+10;
ll n,a[maxn],b[maxn],a_t[maxn],b_t[maxn],tree[maxn],c[maxn],pos[maxn];
ll ans;
inline void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        tree[x]%=mod;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        sum%=mod;
        x-=lowbit(x);
    }
    return sum;
}

int main()
{
    cin>>n;
    rep(i,1,n){
        cin>>a[i];
        a_t[i]=a[i];
    }
    rep(i,1,n){
        cin>>b[i];
        b_t[i]=b[i];
    }
    sort(a_t+1,a_t+1+n);
    sort(b_t+1,b_t+1+n);
    int a_m=unique(a_t+1,a_t+1+n)-a_t-1;
    int b_m=unique(b_t+1,b_t+1+n)-b_t-1;
    rep(i,1,n){
        a[i]=lower_bound(a_t+1,a_t+1+a_m,a[i])-a_t;
        pos[a[i]] = i;
        b[i]=lower_bound(b_t+1,b_t+1+b_m,b[i])-b_t;
    }
    rep(i,1,n) c[i]=pos[b[i]];
    rep(i,1,n){
        add(c[i],1);
        ans=(ans+i-sum(c[i]))%mod;
    }
    cout<<ans<<endl;
    return 0;
}

NEFU477 不同的卡片-树状数组

这个题一开始卡了我好久。

  • 因为这个题不能sort+unique去重,这样会改变相对顺序。所以只能在输入的时候边标记边用树状数组维护,遇到重复的就不计数。
  • 更不能离散化,因为这里不能转化为相对顺序。
  • 要开long long。
#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
ll n,p,x,a[maxn],tree[maxn];
ll vis[maxn];

void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}

ll cnt;


int main()
{
    while(~scanf("%lld%lld",&n,&p)){
        cnt++;
        memset(a,0,sizeof(a));
        memset(vis,0,sizeof(vis));
        memset(tree,0,sizeof(tree));
        printf("Case #%lld:\n",cnt);
        rep(i,1,n){
            scanf("%lld",&x);
            if(!vis[x]) {
                add(i,x);
                vis[x]=1;
                a[i]=sum(i);
            }
            else{
                a[i]=a[i-1];
            }
        }
        rep(i,1,p){
            scanf("%lld",&x);
            printf("%lld\n",a[x]);
        }
    }
    return 0;

}

NEFU478 排队-树状数组

和小鱼比可爱类似,离散化去重即可。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],t[maxn],tree[maxn],b[maxn],p,x;
void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
int cnt;

int main()
{
    while(~scanf("%d%d",&n,&p)){
        cnt++;
        memset(a,0,sizeof(a));
        memset(t,0,sizeof(t));
        memset(tree,0,sizeof(tree));
        rep(i,1,n){
            scanf("%d",&a[i]);
            t[i]=a[i];
        }
        sort(t+1,t+1+n);
        int m=unique(t+1,t+1+n)-t-1;
        rep(i,1,n){
            a[i]=lower_bound(t+1,t+1+m,a[i])-t;
        }
        rep(i,1,n){
            add(a[i],1);
            b[i]=sum(a[i]-1);
        }
        printf("Case #%d:\n",cnt);
        rep(i,1,p){
            scanf("%d",&x);
            printf("%d\n",b[x]);
        }
    }
    return 0;
}

NEFU479 神奇的字符串-树状数组

用树状数组维护两个字符串相等的数量即可。
输入用cin然后取消同步
输出用printf即可,比cout取消同步要快。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
int n,m,tree[maxn];
char a[maxn],b[maxn],c;
void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
int cnt,op,id,p,l,r;

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m){
        cnt++;
        memset(tree,0,sizeof(tree));
        rep(i,1,n) cin>>a[i];
        rep(i,1,n) {
            cin>>b[i];
            if(a[i]==b[i]) add(i,1);
        }
        printf("Case #%d:\n",cnt);
        rep(i,1,m){
            //scanf("%d",&op);
            cin>>op;
            if(!op) {
                cin>>l>>r;
                //scanf("%d%d",&l,&r);
                printf("%d\n",sum(r)-sum(l-1));
            }
            else {
                cin>>id>>p>>c;
                if(id==1){
                    if(a[p]==b[p]&&a[p]!=c) add(p,-1);
                    if(a[p]!=b[p]&&b[p]==c) add(p,1);
                    a[p]=c;
                }
                if(id==2){
                    if(a[p]==b[p]&&b[p]!=c) add(p,-1);
                    if(a[p]!=b[p]&&a[p]==c) add(p,1);
                    b[p]=c;
                }
            }
        }
    }
    return 0;
}

NEFU480 询问区间和-树状数组

可以拿走的标记一下,清0即可。但并不是代表值为0的一定是拿走过的。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
typedef long long ll;
ll n,m,tree[maxn],a[maxn];
int vis[maxn],cnt,op;
void add(ll x,ll d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
ll sum(ll x)
{
    ll sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
ll l,r,x;
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m){
        cnt++;
        memset(tree,0,sizeof(tree));
        memset(vis,0,sizeof(vis));
        rep(i,1,n) {cin>>a[i];add(i,a[i]);}
        printf("Case #%d:\n",cnt);
        rep(i,1,m){
            cin>>op;
            if(op) {
                cin>>l>>r;
                l++;r++;
                printf("%lld\n",sum(r)-sum(l-1));
            }
            else {
                cin>>x;
                x++;
                if(vis[x]) printf("Sorry\n");
                else{
                    add(x,-a[x]);//将此处清0
                    vis[x]=1;
                    printf("%lld\n",a[x]);
                }
            }
        }
    }
    return 0;
}

NEFU1464 数星星-树状数组

题目大意就是:一个星星左边和下面有多少颗星星,就是多少级别的星星。

  • 因为数据是按y升序输入的,所以就不用管下面的星星有多少了,只管左边的星星即可,所以用树状数组维护x的位置即可。
  • 因为x可以取到0,而树状数组必须从1开始,所以一开始x++
#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=32010;
int n,a[maxn],tree[maxn],x,y;

void add(int x,int d)
{
    while(x<=maxn){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}


int main()
{
    scanf("%d",&n);
    rep(i,1,n){
        scanf("%d%d",&x,&y);
        x++;//x,y从0开始的
        a[sum(x)]++;
        add(x,1);
    }
    rep(i,0,n-1){
        printf("%d\n",a[i]);
    }
    return 0;
}

NEFU1466 清点人数-树状数组

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],tree[maxn],m,p,k;

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
char op;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    rep(i,1,k){
        cin>>op;
        if(op=='A'){
            cin>>m;
            printf("%d\n",sum(m));
        }
        if(op=='B'){
            cin>>m>>p;
            add(m,p);
        }
        if(op=='C'){
            cin>>m>>p;
            add(m,-p);
        }
    }
    return 0;
}

NEFU1471 水题–树状数组

翻转相同问题可以考虑翻转的奇偶。
如果翻转次数为偶数,那么相当于没翻转。
如果翻转次数为奇数,那么相当于翻转一次。
用树状数组维护翻转次数即可。
利用差分思想进行区间更新。

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],tree[maxn],m,p,l,r;

void add(int x,int d)
{
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;


int main()
{
    n=read();m=read();
    rep(i,1,m){
        p=read();
        if(p==1){
            l=read();r=read();
            add(l,1);
            add(r+1,-1);
        }
        if(p==2){
            l=read();
            if(sum(l)&1) printf("1\n");
            else printf("0\n");
        }
    }
    return 0;
}

完结。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NEFU AB-IN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值