加载中...

树状数组 单点修改 区间查询(查询从1到k的属性 )

单调修改区间查询(两者相减)

离散化 +树状数组 +LIS

最大上升子序列和
需要查询

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long LL;
const int N = 100010;

int n;
int w[N];
LL tr[N];
vector<int> xs;
LL f[N];

int get(int x)
{
    return lower_bound(xs.begin(), xs.end(), x) - xs.begin() + 1;//存是在0开始存的 但返回的时候是1开始返回的
}

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

void add(int x, LL v)
{
    for (int i = x; i <= n; i += lowbit(i))
        tr[i] = max(tr[i], v);
}

LL query(int x)
{
    LL res = 0;
    for (int i = x; i; i -= lowbit(i))
        res = max(res, tr[i]);
    return res;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &w[i]);
        xs.push_back(w[i]);//保存所有存储的元素
    }
    sort(xs.begin(), xs.end());//将所有元素大小排序
    xs.erase(unique(xs.begin(), xs.end()), xs.end());//删除重复的元素 这里的xs还是存的元素而不是下表 用于find的找

    LL res = 0;
    for (int i = 0; i < n; i ++ )//这里的数是n个a数组
    {
        int k = get(w[i]);//找到w[i]元素在xs数组对应的映射的下标   
        f[i] = query(k - 1) + w[i];//query查询xs的下标 
        res = max(res, f[i]);//更新最大值
        add(k, f[i]);//在tr第k个位置插入
    }

    printf("%lld\n", res);
    return 0;
}

楼兰图腾https://www.acwing.com/problem/content/243/

看看某个点的高度 他的左边和右边的点有多少个点比他高的
tr[i]为i下标点的高度 每次查询完add对某个高度 +1
就可以完成看看左边的操作
看左边的高度 从1~n 看右边的高度 从n~1

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5+10;
int tr[N],a[N];
int great[N],lower[N];
int lowbit (int x){
    return x&-x;
}
void add(int x,int v){
    for (int i = x; i <N; i +=lowbit(i) ) tr[i]+=v;
}
int query(int x){
    int res=0;
    for (int i = x; i ; i -=lowbit(i) ){
        res+=tr[i];
    }
    return res;
}
signed main()
{
    int n;
    cin>>n;
    for (int i = 1; i <= n; i ++ ){
        cin >> a[i];
    }
    for (int i = 1; i <= n; i ++ ){
        great[i]+=query(n)-query(a[i]);
        lower[i]+=query(a[i]-1);
        add(a[i],1);//tr[i] i小标表示的是高度
    }
    memset(tr, 0, sizeof tr);
    int res=0,ans=0;
    for (int i = n; i >=1 ; i -- ){
        res+=great[i]*(query(n)-query(a[i]));
        ans+=lower[i]*(query(a[i]-1));
        add(a[i], 1);
    }
    cout << res<<" "<<ans;
    return 0;
}

数星星https://www.acwing.com/problem/content/1267/
还是看看左边的高度

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long 
const int N = 32005;
int n;
int tr[N];
int lowbit(int x){
    return x&-x;
}
void  add(int x,int v){
    for (int i = x; i < N; i +=lowbit(i) ){
        tr[i]+=v;
    }
    // return ;
}
int query(int x){
    int res=0;
    for (int i = x; i; i -=lowbit(i) ){
        res+=tr[i];
    }
    return res;
}

int q[N];
signed main(){
    cin>>n;
    for (int i = 1; i <= n; i ++ ){
        int x,y;scanf("%d%d",&x,&y);x++;
        // cout << query(x)<<endl;
        q[query(x)]++;
        add(x,1 );
    }
    for (int i = 0; i < n; i ++ ){
        // cout <<q[i] <<"\n";
        printf("%d\n",q[i]);
    }
    return 0;
}

区间修改 单点查询

一个简单的整数的问题

https://www.acwing.com/problem/content/248/
区间加数: 将原数组转为差分数组 tr[]里面存储的是a[]的差分数组 然后再对两个端点进行经典的差分数组加数

一个简单的整数问题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int tr[N],a[N];
int n,m;
int lowbit(int x){
    return x&-x;
}
void add(int x,int v){
    for (int i = x; i <= n; i +=lowbit(i) ){
        tr[i]+=v;
    }
}
int  ask(int x){
    int res=0;
    for (int i = x; i ; i -=lowbit(i) ){
        res+=tr[i];
    }
    return res;
}

int main()
{
    cin >> n>>m;
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ ) add(i,a[i]-a[i-1]);
    
    for (int i = 0; i < m; i ++ ){
        string s;
        cin >> s;
        if(s=="C"){
            int a,b,c;scanf("%d%d%d",&a,&b,&c);
            
            add(a, c);add(b+1,-c);
        }else{
            int a;cin>>a;
            cout << ask(a)<<endl;//对差分数组求和的值就是这个点的的值
                       
        }
    }

    return 0;
}

区间修改 区间查询(与上面的区别主要在修改 差分的前缀和的前缀和)


原理如图 主要通过在加一行(b1..bx)和一个tr2[i]数组来处理出相关内容
tr1[1]//b[i]的前缀和
tr2[]//b[i]i的前缀和
与上面的区间修改单点查询大部分内容相同
要注意 修改的时候 每次需要修改两个数组的差分端点 一个数组是l,d和r+1,d ,一个是l
d,(r+1)*d

区间查询代码:
LL prefix_sum(int x){
  return sum(tr1,x)*(x+1)-sum(tr2,x);//两个sum是一般的操作代码 需要传出数组名到第是哪个地方需要
}

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n, m;
int a[N];
LL tr1[N];  // 维护b[i]的前缀和
LL tr2[N];  // 维护b[i] * i的前缀和

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

void add(LL tr[], int x, LL c)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

LL sum(LL tr[], int x)
{
    LL res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

LL prefix_sum(int x)
{
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ )
    {
        int b = a[i] - a[i - 1];
        add(tr1, i, b);
        add(tr2, i, (LL)b * i);
    }

    while (m -- )
    {
        char op[2];
        int l, r, d;
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'Q')
        {
            printf("%lld\n", prefix_sum(r) - prefix_sum(l - 1));
        }
        else
        {
            scanf("%d", &d);
            // a[l] += d
            add(tr1, l, d), add(tr2, l, l * d);
            // a[r + 1] -= d
            add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);
        }
    }

    return 0;
}


posted @ 2022-05-21 16:48  liang302  阅读(98)  评论(0编辑  收藏  举报