复健4+5

复健计划(4)并查集

因为前段时间刚刚复习了几道并查集题目,还算比较熟悉,就先放在这里:

板子题(程序自动分析,NOI2015)
带边权的并查集1(POJ 1733)
带边权的并查集2(银河英雄传说,NOI2002)
带扩展域的并查集(POJ 1733)

复健计划(5)树状数组

树状数组是一个好用,好写但是代码比较抽象的数据结构,总之记住 c [ x ] c[x] c[x] 表示 a a a 数组 [ x − l o w b i t ( x ) + 1 , x ] [x-lowbit(x)+1,x] [xlowbit(x)+1,x]的和,其中 l o w b i t ( x ) lowbit(x) lowbit(x) 表示二进制表示下从右往左第一个1和末位的0所构成的数值.

1、最基础的题 acwing 242

题目大意:区间修改,单点查询
思路:用树状数组维护原序列的差分序列,注意修改时要保证r+1~n部分的数字不变.
代码:

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

using namespace std;

const int N=100010;

int a[N],n,m;
int b[N];
int c[N];

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

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

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

int main(){
    
    cin>>n>>m;
    
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]-a[i-1];
    
    for(int i=1;i<=n;i++) add(i,b[i]);
    
    for(int i=1;i<=m;i++){
        int l,r,d,x;
        char op[2];
        scanf("%s",op);
        if(op[0]=='Q'){
            scanf("%d",&x);
            printf("%d\n",sum(x));
        }
        else{
            scanf("%d%d%d",&l,&r,&d);
            add(l,d);
            add(r+1,-d);
        }
    }
    
    return 0;
}
2、需要提取模型的题目 acwing 241

题目大意:给一个1~n的排列,求三元组 ( a , b , c ) (a,b,c) (a,b,c)满足条件 a < b , b > c a<b,b>c a<b,b>c的个数;
思路:针对该排列中任意一个位置 i i i ,其高度为 y i y_i yi ,则要求找到 1   i − 1 1~i-1 1 i1 位置中比 y i y_i yi 大的数的个数和 i + 1   n i+1~n i+1 n 位置中比 y i y_i yi 大的个数,相乘即可;具体实现时,求前者时循环从1到n,这样不会算入i后满足条件的高度数量,求后者时同理;并且此题以高度为原数组,这就是转换,也是难点.
代码:

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

using namespace std;

const int N=200010;

typedef long long ll;

int a[N],n;
int c[N];
ll gr[N],le[N];
ll ans1,ans2;

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

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

ll sum(int x){
    ll ans=0;
    for(;x;x-=lowbit(x)) ans+=(ll)c[x];
    return ans;
}

int main(){
    
    cin>>n;
    
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    
    for(int i=1;i<=n;i++){ 
        add(a[i],1);
        gr[i]=sum(n)-sum(a[i]);
        le[i]=sum(a[i]-1);
       
    }
    
    memset(c,0,sizeof(c));
    
    for(int i=n;i;i--){
        add(a[i],1);
        ans1+=gr[i]*(sum(n)-sum(a[i]));
        ans2+=le[i]*sum(a[i]-1);
        
    }
    
    cout<<ans1<<" "<<ans2<<endl;
    
    return 0;
}
3、更加有技巧的两道题目:

区间修改区间求和
二分+树状数组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值