[模板]树状数组

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 x

  • 求出某区间每一个数的和

输入格式

第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n-1 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。

接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 x 个数加上 k

  • 2 x y 含义:输出区间 [x,y]内每个数的和

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

输入输出样例

输入 #1
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
输出 #1
14
16

说明/提示

【数据范围】

对于 30% 的数据,1≤n≤81 \le n \le 81n8,1≤m≤101\le m \le 101m10;
对于 70% 的数据,1≤n,m≤1041\le n,m \le 10^41n,m104;
对于 100% 的数据,1≤n,m≤5×1051\le n,m \le 5\times 10^51n,m5×105。

#include<bits/stdc++.h>
using namespace std;
int m,n,a[510010],x,y,op;
inline int lowbit(int pos)
{
    return pos&(-pos);
}
inline void add(int pos,int x)
{
    while(pos<=n)
    {
        a[pos]+=x;
        pos+=lowbit(pos);
    }
}
inline int query(int pos)
{
    int ans=0;
    while(pos>0)
    {
        ans+=a[pos];
        pos-=lowbit(pos);
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)
        {
            add(x,y);
        }
        else
        {
            printf("%d\n",query(y)-query(x-1));
        }
    }
}

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的值

输入格式

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含2或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x 含义:输出第x个数的值

输出格式

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入 #1
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4
输出 #1
6
10

说明/提示

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

#include<bits/stdc++.h>
using namespace std;
int m,n,a[510010],x,y,op,b[510010],k;
inline int lowbit(int pos)
{
    return pos&(-pos);
}
inline void add(int pos,int x)
{
    while(pos<=n)
    {
        a[pos]+=x;
        pos+=lowbit(pos);
    }
}
inline int query(int pos)
{
    int ans=0;
    while(pos>0)
    {
        ans+=a[pos];
        pos-=lowbit(pos);
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i]);
        add(i,b[i]-b[i-1]);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&op,&x);
        if(op==1)
        {
            scanf("%d%d",&y,&k); 
            add(x,k);
            add(y+1,-k);
        }
        else
        {
            printf("%d\n",query(x));
        }
    }
}

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。

输入格式

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。序列中每个数字不超过10910^9109

输出格式

给定序列中逆序对的数目。

输入输出样例

输入 #1
6
5 4 2 6 3 1
输出 #1
11

说明/提示

对于25%的数据,n≤2500n \leq 2500n2500

对于50%的数据,n≤4×104n \leq 4 \times 10^4n4×104。

对于所有数据,n≤5×105n \leq 5 \times 10^5n5×105

请使用较快的输入输出

(注意离散化要判断是否有相同的数据)

#include<bits/stdc++.h>
using namespace std;
struct tree
{
    int val;
    int pos;
}b[510010];
int m,n,x,y,op,a[510010],c[510010],cnt;
long long int ans;
bool compare1(tree a,tree b)
{
    return a.val<b.val;
}
inline int lowbit(int pos)
{
    return pos&(-pos);
}
inline void add(int pos,int x)
{
    while(pos<=n)
    {
        a[pos]+=x;
        pos+=lowbit(pos);
    }
}
inline long long int query(int pos)
{
    long long int ans=0;
    while(pos>0)
    {
        ans+=a[pos];
        pos-=lowbit(pos);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&b[i].val);
        b[i].pos=i;
    }
    sort(b+1,b+n+1,compare1);
    for(int i=1;i<=n;i++)
    {
        if(b[i].val!=b[i-1].val) cnt++;
        c[b[i].pos]=cnt;        
    }
    for(int i=1;i<=n;i++)
    {
        add(c[i],1);
        ans+=i-query(c[i]);
    }
    printf("%lld\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/satans/p/11953335.html
今日推荐