树状数组的题单及代码详解

注:本题单并非按照难度升序排的,而是按照个人做题时间排的

敌兵布阵 单点修改 区间查询 求和

P3374 【模板】树状数组 1 单点修改 区间查询 求和

P3368 【模板】树状数组 2 区间修改 单点查询

A Simple Problem with Integers 区间修改 单点查询

毒瘤数据结构题 单点修改 区间查询

I Hate It 单点修改 区间查询 求max

P1908 逆序对 离散化 单点修改 区间查询 求和

P2068 统计和 单点修改 区间查询 求和

P1816 忠诚 区间查询min

P1198 [JSOI2008]最大数 末尾区间查询max

[SDOI2009]HH的项链 查询l到r不同的数字的个数,区间查询,单点修改

[HEOI2012]采花 查询l到r出现次数大于2的数字的个数,区间查询,单点修改

敌兵布阵

题目描述:

单点修改

区间查询

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 50000 + 50
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int t, n, x, y, c;
int tr[MAX];
string s;

inline int lowbit(int x){
    return x & (-x);
}

inline void update(int i, int c){
    while (i <= n) {
        tr[i] += c;
        i += lowbit(i);
    }
}

inline int getnum(int i){
    int ans = 0;
    while (i > 0) {
        ans += tr[i];
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    io;
    cin>>t;
    for(int p = 1; p <= t; ++p){
        mem(tr, 0);
        cout<<"Case "<<p<<":\n";
        cin>>n;
        for(int i = 1; i <= n; ++i){
            cin>>x;
            update(i, x);
        }
        while (cin>>s && s[0] != 'E') {
            cin>>x>>y;
            if(s[0] == 'Q'){
                cout<<getnum(y) - getnum(x - 1)<<endl;
            }
            else if(s[0] == 'A'){
                update(x, y);
            }
            else {
                update(x, -y);
            }
        }
    }
    return 0;
}	

P3374 【模板】树状数组 1

题目描述:

单点修改 + 区间查询

思路

最板子的树状数组,不解释

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 5000000 + 50
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int n, m, x, y, op;
int tr[MAX];

inline int lowbit(int x){
    return x & (-x);
}

inline void add(int i, int c){
    while (i <= n) {
        tr[i] += c;
        i += lowbit(i);
    }
}

inline int getans(int i){
    int ans = 0;
    while (i > 0) {
        ans += tr[i];
        i -= lowbit(i);
    }
    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{
            cout<<getans(y) - getans(x - 1)<<endl;
        }
    }
    return 0;
}

P3368 【模板】树状数组 2

题目描述:

区间修改 + 单点查询

思路:

这个题是树状数组的一种变形

朴素版的树状数组如果想进行区间修改,肯定是对每个点都去修改一下,但是这样根本没法A,所以得想办法改进一下

这里引入差分数组,差分数组的d[i] = tr[i] - tr[i - 1],也就是用原数组的当前的值减去前面一个的值

例如:

  • tr[] = 1 2 3 5 6 9

  • d[] = 1 1 1 2 1 3

这里有一个结论:原数组[l, r]的区间进行加减操作,其差分数组仅仅只有x, y + 1这两个位置的值发生变化,这里不多解释,拿上面的例子来说:对上述tr[]数组的[2, 5]加2得

  • tr[] = 1 4 5 7 8 9
  • d[] = 1 3 1 2 1 1

所以我们只要构造的是差分数组的树状数组就可以只进行两次修改,而单点查询i的值,其实就等于差分数组[1, i]的所有的值的和,完美符合树状数组的getans函数,perfect!

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 500000 + 50
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int n, m, x, y, op, c;
int tr[MAX];
int d[MAX];

inline int lowbit(int x){
    return x & (-x);
}

inline void add(int i, int c){
    while (i <= n) {
        d[i] += c;
        i += lowbit(i);
    }
}

inline int getans(int i){
    int ans = 0;
    while (i > 0) {
        ans += d[i];
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i){
        scanf("%d", &tr[i]);
        add(i, tr[i] - tr[i - 1]);
    }
    for(int i = 1; i <= m; ++i){
        scanf("%d", &op);
        if(op == 1){
            scanf("%d%d%d", &x, &y, &c);
            add(x, c);
            add(y + 1, -c);
        }
        else{
            scanf("%d", &x);
            cout<<getans(x)<<endl;
        }
    }
    return 0;
}

A Simple Problem with Integers

题目描述:

区间修改,区间查询

思路:

区间修改区间查询,但是不想写线段树怎么办?树状数组来解决!

和上面区间修改单点查询差不多,类似于利用一次前缀和求区间查询,如查前k个的值就为: ∑ i = 1 k ∑ j = 1 i d [ j ] \sum_{i=1}^{k}\sum_{j=1}^{i}{d[j]} i=1kj=1id[j]

经过如下化简:(为什么在ipad上写字会这么丑???

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAIf5sGe-1632487723218)(/Users/chelsea/Library/Application Support/typora-user-images/image-20210731225650437.png)]

我们只需要用维护两个树状数组,一个sum1,一个sum2即可,sum1[i] = d[i],而sum2[i] = (i - 1) * d[i]

需要注意的是,getans的时候,记得给sum1乘k

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

ll n, m;
ll sum1[MAX], sum2[MAX], tr[MAX];
ll x, y, c;
char p;

inline ll lowbit(ll x){
    return x & (-x);
}

inline void add(ll i, ll c){//单点修改,因为该点影响了好多点,而这个点对sum2的贡献只有固定的 i * c,所以要用x来记录一下i
    ll x = i;
    while (i <= n) {
        sum1[i] += c;
        sum2[i] += c * (x - 1);//
        i += lowbit(i);
    }
}

inline ll getans(ll i){
    ll ans = 0;
    ll x = i;
    while (i > 0) {
        ans += x * sum1[i] - sum2[i];//记得要乘x
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; ++i){
        scanf("%lld", &tr[i]);
        add(i, tr[i] - tr[i - 1]);
    }
    for(int i = 1; i <= m; ++i){
        cin>>p;
        if(p == 'Q'){
            scanf("%lld%lld",&x, &y);
            cout<<getans(y) - getans(x - 1)<<endl;
        }
        else{
            scanf("%lld%lld%lld", &x, &y, &c);
            add(x, c);
            add(y + 1, -c);
        }
    }
    return 0;
}

毒瘤数据结构题

题目描述:

给你一个长为n,初始全为0的序列,根据输入进行n次以下操作:

  • “1 x” 表示把x位置修改为1
  • “2 x” 表示查询,如果将x位置修改成1,求最大的i满足序列位置1到i-1上的所有值全为1

思路:

单点修改,区间查询

这不树状数组板子题?其实不然,因为查询的时候有一步假装x位置为1,这里不好处理,不过我们可以用二分+树状数组,每次查询的时候去二分即可

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 5000000 + 50
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int t, n, x, y, c;
int tr[MAX];
bool vis[MAX];
string s;

inline int lowbit(int x){
    return x & (-x);
}

inline void update(int i, int c){
    while (i <= n) {
        tr[i] += c;
        i += lowbit(i);
    }
}

inline int getnum(int i){
    int ans = 0;
    while (i > 0) {
        ans += tr[i];
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    n = IntRead();
    for(int i = 1; i <= n; ++i){
        x = IntRead();y = IntRead();
        if(x == 1){
            if(vis[y] == 0){
                vis[y] = 1;
                update(y, 1);
            }
        }
        else {
            int l = 1, r = n;
            while (l <= r) {
                int mid = (l + r) / 2;
                int k = getnum(mid);
//                cout<<k<<' '<<mid<<endl;
                if(mid >= y && vis[y] == 0)++k;
                if(k < mid)r = mid - 1;
                else l = mid + 1;
            }
            printf("%d\n", l);
        }
//        for(int i = 1; i <= n; ++i)cout<<tr[i]<<' ';
//        cout<<endl;
    }
    return 0;
}

I Hate It

题目描述:

单点修改

区间查询max

思路:

树状数组
思路和求和差不多不过也不一样,ans数组维护的是区间最大值
单点修改的时候,就和求和的操作差不多,不过要改成max
区间查询的时候,我们应该将这个区间,先分成[x, y - 1], 与y
y–,然后用lowbit去缩减y,直到缩到y-lowbit < x,就该停了,因为此时的x就在[y-lowbit, y]这个区间里面,就让y–,再去重复上面的操作,直到x == y就停了

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 200000 + 50
#define MOD 1000000007
#define mod(x) ((x)%MOD)
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int n, m, x, y;
int tr[MAX];
int ans[MAX];
string op;

inline void add(int i, int x){
    tr[i] = x;
    while (i <= n) {
        ans[i] = max(ans[i], x);
        i += lowbit(i);
    }
}

inline int getmax(int x, int y){
    int maxn = tr[y];
    while (x != y) {
        for(y -= 1; y - lowbit(y) >= x; y -= lowbit(y)){
            maxn = max(maxn, ans[y]);
        }
        maxn = max(maxn, tr[y]);
    }
    return maxn;
}

int main(){
    io;
    while (cin>>n>>m) {
        mem(ans, 0);
        for(int i = 1; i <= n; ++i){
            cin>>x;
            add(i, x);
        }
        for(int i = 1; i <= m; ++i){
            cin>>op>>x>>y;
            if(op == "Q"){
                cout<<getmax(x, y)<<endl;
            }
            else add(x, y);
        }
    }
    
    return 0;
}


P1908 逆序对

思路:

离散化+树状数组

离散化的数组为将原数组从大到小排序后每个数在原数组的位置,如果这个数出现不止一次,则按位置从大到小排

如 6 6 6 6 6 6

应该排成 6 5 4 3 2 1而不是1 2 3 4 5 6

离散化后的数组为tr

此时逆序对的个数就等于找所有满足 t r [ i ] > t r [ j ] , i > j tr[i]>tr[j], i>j tr[i]>tr[j],i>j的值,就是找位置 i 前面的小于tr[i]的数量,然后加起来即可

这就有树状数组的发挥空间了,我们只需要从头开始遍历,对于每个tr[i],进行两个操作

  • update(tr[i])在树状数组的树上给 tr[i] 的位置的+1
  • sum += getans(tr[i] - 1) 更新答案,减1是因为自己不能和自己构成逆序对

然后最后输出sum就可以了

记得开longlong

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 500000 + 50
//#define mod 1000000007
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

ll n;
struct ran{
    ll id, x;
}tr[MAX];
ll tree[MAX];

bool cmp(ran a, ran b){//排序
    if(a.x != b.x)return a.x > b.x;
    else return a.id > b.id;
}

inline void update(ll i){
    while (i <= n) {
        tree[i] += 1;
        i += lowbit(i);
    }
}

inline int getans(ll i){
    int ans = 0;
    while (i > 0) {
        ans += tree[i];
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    io;
    cin>>n;
    for(int i = 1; i <= n; ++i){
        cin>>tr[i].x;
        tr[i].id = i;
    }
    sort(tr + 1, tr + 1 + n, cmp);
    ll sum = 0;
    for(int i = 1; i <= n; ++i){
        sum += getans(tr[i].id - 1);
        update(tr[i].id);
    }
    cout<<sum<<endl;
    return 0;
}


P2068 统计和

思路

单点修改 区间查询

水题

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 500000 + 50
//#define mod 1000000007
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

ll n, m;
ll x, y;
char op;
ll tr[MAX];

inline void update(ll i, ll c){
    while (i <= n) {
        tr[i] += c;
        i += lowbit(i);
    }
}

inline ll getans(ll i){
    ll ans = 0;
    while (i > 0) {
        ans += tr[i];
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    io;
    cin>>n>>m;
    while (m--) {
        cin>>op>>x>>y;
        if(op == 'x'){
            update(x, y);
        }
        else{
            cout<<getans(y) - getans(x - 1)<<endl;
        }
    }
    return 0;
}

P1816 忠诚

题目描述:

区间查询最小值

思路:

和上面区间查询最大值一样,不过要手动给minx数组初始化为inf

还有要注意输出是以空格隔开(手动微笑

#include<map>#include<set>#include<stack>#include<queue>#include<cmath>#include<cstdio>#include<string>#include<vector>#include<sstream>#include<cstring>#include<stdlib.h>#include<iostream>#include<algorithm>using namespace std;#define endl '\n'#define inf 0x3f3f3f3f#define MAX 100000 + 50//#define mod 1000000007#define lowbit(x) (x & (-x))#define sd(n) scanf("%d",&n)#define sdd(n,m) scanf("%d %d",&n,&m)#define pd(n) printf("%d\n", (n))#define pdd(n,m) printf("%d %d\n",n, m)#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)#define mem(a,b) memset((a),(b),sizeof(a))typedef  long long ll ;typedef unsigned long long ull;//不开longlong见祖宗!提交不看数据范围见祖宗!inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}int n, m, x, y;int tr[MAX];int minx[MAX];inline void update(int i, int c){    tr[i] = c;    while (i <= n) {        minx[i] = min(minx[i], c);        i += lowbit(i);    }}inline int getmin(int l, int r){    int mi = tr[r];    while(l != r){        for(r -= 1; r - lowbit(r) >= l; r -= lowbit(r)){            mi = min(mi, minx[r]);        }        mi = min(mi, tr[r]);    }    return mi;}int main(){    io;    cin>>n>>m;    for(int i = 1; i <= n; ++i)minx[i] = inf;    for(int i = 1; i <= n; ++i){        cin>>x;        update(i, x);    }        while (m--) {        cin>>x>>y;        cout<<getmin(x, y)<<' ';    }    return 0;}

P1198 [JSOI2008]最大数

题目描述:

两种操作:

  • Q L 查询当前数列中末尾L个数中最大的数,并输出
  • A n 将n加上t(t是最近一次查询操作的答案,如果未查询则是0),对mod取模,将得到的答案插入到数列的末尾

思路:

末尾区间查询,转换一下,这不就是相当于头区间查询么,这不就是树状数组么,问题解决了

我们用一个tot存到现在插入几个了,然后每次插入就插到200000 - tot的位置就可以了,其他的和普通树状数组一样,+换成max()就可以

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 200000 + 50
//#define mod 1000000007
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!提交不看数据范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int n, mod, tot, x, last;
int tr[MAX];
char op;

inline void update(int i, int c){
    while (i <= 200000) {
        tr[i] = max(tr[i], c);
        i += lowbit(i);
    }
}

inline int getmaxn(int i){
    int ans = 0;
    while (i > 0) {
        ans = max(ans, tr[i]);
        i -= lowbit(i);
    }
    return ans;
}

int main(){
    io;
    cin>>n>>mod;
    for(int i = 1; i <= n; ++i){
        cin>>op>>x;
        if(op == 'A'){
            update(200000 - tot, (x + last) % mod);
            ++tot;
        }
        else{
            last = getmaxn(200000 - tot + x);
            cout<<last<<endl;
        }
    }
    return 0;
}

[SDOI2009]HH的项链

题目描述:

查询l到r中不同的数字的个数

思路:

离线使用树状数组

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;

//#pragma GCC optimize("Ofast")
//#pragma GCC target("fma,sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
//#pragma GCC optimize("unroll-loops")

#define eps 1e-8
#define endl '\n'
#define inf 0x3f3f3f3f
#define NMAX 1000 + 50
#define MAX 1000000 + 50
#define mod 1000000007
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
#define m_p(a,b) make_pair(a, b)
//#define max(a,b) (((a)>(b)) ? (a):(b))
//#define min(a,b) (((a)>(b)) ? (b):(a))

typedef  long long ll;
typedef pair <int,int> pii;
typedef unsigned long long ull;
//不开longlong见祖宗!不看范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}


int n, m;
int tr[MAX];
int sum[MAX];
int pos[MAX];
int ans[MAX];

struct ran{
    int l, r;
    int id;
}ar[MAX];
bool cmp(ran a, ran b){
    return a.r < b.r;
}

inline void update(int i, int c){
    for(; i <= n; i += lowbit(i))sum[i] += c;
}

inline int getans(int x){
    int ans = 0;
    for(int i = x; i; i -= lowbit(i))ans += sum[i];
    return ans;
}


int main(){
    sd(n);
    for(int i = 1; i <= n; ++i)sd(tr[i]);
    sd(m);
    for(int i = 1; i <= m; ++i){
        sdd(ar[i].l, ar[i].r);
        ar[i].id = i;
    }
    sort(ar + 1, ar + 1 + m, cmp);
    int p = 1;
    for(int i = 1; i <= m; ++i){
        for(; p <= ar[i].r; ++p){
            if(pos[tr[p]]){
                update(pos[tr[p]], -1);
            }
            update(p, 1);
            pos[tr[p]] = p;
        }
        ans[ar[i].id] = getans(ar[i].r) - getans(ar[i].l - 1);
    }
    for(int i = 1; i <= m; ++i){
        cout<<ans[i]<<endl;
    }
    return 0;
}

[HEOI2012]采花

题目描述:

n个数,m次询问,每次询问都给出l和r,问l到r中出现次数大于2的数有多少种

思路:

和上个题差不多

不过这次要求是出现次数大于两次的

当r固定时,看一个数x能不能产生贡献主要是看倒数第二个的x的位置与l的关系

所以我们可以用last1代表倒数第二个的x的位置,last2代表倒数第一个x的位置,对所有询问从r 从小到大排序,离线查询,用树状数组维护即可

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;

//#pragma GCC optimize("Ofast")
//#pragma GCC target("fma,sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native")
//#pragma GCC optimize("unroll-loops")

#define eps 1e-8
#define endl '\n'
#define inf 0x3f3f3f3f
#define NMAX 1000 + 50
#define MAX 1000000 + 50
#define mod 1000000007
#define lowbit(x) (x & (-x))
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define pd(n) printf("%d\n", (n))
#define pdd(n,m) printf("%d %d\n",n, m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
#define m_p(a,b) make_pair(a, b)
//#define max(a,b) (((a)>(b)) ? (a):(b))
//#define min(a,b) (((a)>(b)) ? (b):(a))

typedef  long long ll;
typedef pair <int,int> pii;
typedef unsigned long long ull;
//不开longlong见祖宗!不看范围见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}

int n, m, c;
int tr[MAX];
int last1[MAX], last2[MAX];
int ans[MAX];
int sum[MAX];

struct ran{
    int l, r, id;
}ar[MAX];

bool cmp(ran a, ran b){
    return a.r < b.r;
}

inline void update(int i, int c){
    for(;i <= n; i += lowbit(i))sum[i] += c;
}

inline int getans(int x){
    int ans = 0;
    for(int i = x;i; i -= lowbit(i))ans += sum[i];
    return ans;
}

void work(){
    sddd(n, c, m);
    for(int i = 1; i <= n; ++i)sd(tr[i]);
    for(int i = 1; i <= m; ++i){
        sdd(ar[i].l, ar[i].r);
        ar[i].id = i;
    }
    sort(ar + 1, ar + 1 + m, cmp);
    int k = 1;
    for(int i = 1; i <= m; ++i){
        for(; k <= ar[i].r; ++k){
            if(!last1[tr[k]]){
                last1[tr[k]] = k;
            }
            else{
                if(!last2[tr[k]]){
                    update(last1[tr[k]], 1);
                    last2[tr[k]] = k;
                }
                else{//1 2 3
                    update(last1[tr[k]], -1);
                    update(last2[tr[k]], 1);
                    last1[tr[k]] = last2[tr[k]];
                    last2[tr[k]] = k;
                }
            }
        }
        ans[ar[i].id] = getans(ar[i].r) - getans(ar[i].l - 1);
    }
    for(int i = 1; i <= m; ++i)cout<<ans[i]<<endl;
}

int main(){
//    int tt;cin>>tt;
//    for(int _t = 1; _t <= tt; ++_t){
//        printf("Case #%d: ", _t);
        work();
//    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值