2020-09-11 树状数组求最大值

http://icpc.upc.edu.cn/status.php?user_id=2019UPC135&cid=1461

题目描述

现在请求你维护一个数列,要求提供以下两种操作:1、查询操作。语法:QL功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。2、插入操作。语法:An功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。注意:初始时数列是空的,没有一个数。

输入

第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足D在longint内。接下来M行,查询操作或者插入操作。

输出

对于每一个询问操作,输出一行。该行只有一个数,即序列中最后L个数的最大数。

样例输入
5 100
A 96
Q 1
A 97
Q 1
Q 2
样例输出
96
93
96

树状数组维护数组求最值
附一张图帮助理解代码
在这里插入图片描述

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define inf 0x3f3f3f
using namespace std;
typedef long long ll;
ll c[400500]={0};
ll h[400500]={0};
ll lowbit(ll x)
{
    return x&(-x);
}
ll getmax(ll l,ll r)
{
    ll max1=h[r];
    while(l<=r)
    {
        max1=max(max1,h[r]);
        for(--r;r-l>=lowbit(r);r-=lowbit(r))
            max1=max(max1,c[r]);
    }
    return max1;
}
char s[100];
int main()
{
    ll n,d,a,t=0,cnt=0;
    scanf("%lld%lld",&n,&d);
    while(n--)
    {
        scanf("%s%lld",s,&a);
        if(s[0]=='A')
        {
            h[++cnt]=(a+t)%d;
            c[cnt]=max(getmax(cnt-lowbit(cnt)+1,cnt-1),h[cnt]);
        }
        else
        {
            t=getmax(cnt-a+1,cnt);
            printf("%d\n",t);
        }
    }
}
单点修改+区间查询求和
题目

给定一个数字n,表示数组大小,后有n个数字,表示数组中每个数字的初始值,然后进行m次操作,‘Q’代表查询,查询某一区间所有元素的和,‘C’代表修改,在某一元素上加上值。

输入
10 7
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6
Q 2 4
C 2 4
Q 2 4
输出
4
55
9
15
18

树状数组单点修改+区间求和

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll h[100500]={0};
ll c[100500]={0};
ll lowbit(ll x)
{
    return x&(-x);
}
ll add(ll x,ll a)
{
    while(x<=n)
    {
        c[x]+=a;
        x+=lowbit(x);
    }
}
ll getsum(ll l)
{
    ll sum=0;
    while(l>0)
    {
        sum+=c[l];
        l-=lowbit(l);
    }
    return sum;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&h[i]);
        add(h[i],i);
    }
    char a[100];
    ll b,c;
    while(m--)
    {
        scanf("%s%lld%lld",a,&b,&c);
        if(a[0]=='Q')
        {
            printf("%lld\n",getsum(c)-getsum(b-1));
        }
        else
        {
            add(b,c);
        }
    }
    return 0;
}
区间修改+区间查询

http://icpc.upc.edu.cn/problem.php?cid=1461&pid=13

题目描述

You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

输入

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

输出

You need to answer all Q commands in order. One answer in a line.

样例输入
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
样例输出
4
55
9
15

提示
The sums may exceed the range of 32-bit integers.

#include<bits/stdc++.h>
#define inf 0x3f3f3f
using namespace std;
typedef long long ll;
ll num[400500]={0};
ll sum[400500]={0};
ll lazy[400500]={0};
ll n,m,b,c,d;
ll pushup(ll t)
{
    sum[t]=sum[2*t]+sum[2*t+1];
}
ll pushlazy(ll t,ll lz,ll len)
{
    sum[t]+=lz*len;
    lazy[t]+=lz;
}
ll pushdown(ll l,ll r,ll t)
{
    if(lazy[t]!=0)
    {
        ll mid=(l+r)/2;
        pushlazy(2*t,lazy[t],mid-l+1);
        pushlazy(2*t+1,lazy[t],r-mid);
        lazy[t]=0;
    }
}
ll build(ll l,ll r,ll t)
{
    lazy[t]=0;
    if(l==r)
    {
        sum[t]=num[l];
        return 0;
    }
    ll mid=(l+r)/2;
    build(l,mid,t*2);
    build(mid+1,r,2*t+1);
    pushup(t);
}
ll add(ll x,ll l,ll r,ll L,ll R,ll t)
{
    if(l>=L&&r<=R)
    {
        lazy[t]+=x;
        sum[t]+=(r-l+1)*x;
        return 0;
    }
    pushdown(l,r,t);
    ll mid=(l+r)/2;
    if(L<=mid)
        add(x,l,mid,L,R,2*t);
    if(R>mid)
        add(x,mid+1,r,L,R,2*t+1);
    pushup(t);
}
ll querysum(ll l,ll r,ll L,ll R,ll t)
{
    if(l>=L&&r<=R)
        return sum[t];
    pushdown(l,r,t);
    ll mid=(l+r)/2;
    ll ans=0;
    if(L<=mid)
        ans+=querysum(l,mid,L,R,2*t);
    if(R>mid)
        ans+=querysum(mid+1,r,L,R,2*t+1);
    return ans;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    build(1,n,1);
    char a[100]={0};
    for(ll i=1;i<=m;i++)
    {
        scanf("%s",a);
        if(a[0]=='C')
        {
            scanf("%lld%lld%lld",&b,&c,&d);
            add(d,1,n,b,c,1);
        }
        else
        {
            scanf("%lld%lld",&b,&c);
            printf("%lld\n",querysum(1,n,b,c,1));
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值