树状数组

树状数组每个点管理的区间是\([i-lowbit(i)+1,i]\),一般用单点修改区间查询问题。

单点修改区间查询树状数组。

struct BinaryIndexedTree{
    int t[N];
    inline int lowbit(int x){
        return x&-x;
    }
    inline void add(int x,int v){
        while(x<=n)t[x]+=v,x+=lowbit(x);
    }
    inline int query(int x){
        int re=0;
        while(x)re+=t[x],x-=lowbit(x);
        return re;
    }
    inline int ask(int l,int r){
        return query(r)-query(l-1);;
    }
}bit;

区间修改单点查询树状数组。区间修改用差分,差分数组的前缀和就是原来的数组。

inline void add(int l,int r,int x){
    add(l,x);
    add(r+1,-x);
}

区间修改区间查询树状数组,设序列\(a\)的差分数组为\(b\)\(\sum_{i=1}^ra_i=\sum_{i=1}^r\sum_{j=1}^ib_i=\sum_{i=1}^r(b_i(r-i+1))=\sum_{i=1}^rb_i(r+1)-\sum_{i=1}^rb_i*i\),于是可以维护\(\sum b_i\)\(\sum b_i*i\).

struct BinaryIndexedTree{
    int t[N][2];
    inline int lowbit(int x){
        return x&-x;
    }
    inline void modify(int x,int v,int f){
        while(x<=n)t[x][f]+=v,x+=lowbit(x);
    }
    inline int query(int x,int f){
        int re=0;
        while(x)re+=t[x][f],x-=lowbit(x);
        return re;
    }
    inline int sum(int x){
        int re=0;
        return query(x,0)*(x+1)-query(x,1);
    }
    inline void add(int l,int r,int x){
        modify(l,x,0);
        modify(r+1,-x,0);
        modify(l,x*l,1);
        modify(r+1,-x*(r+1),1);
    }
    inline int ask(int l,int r){
        return sum(r)-sum(l-1);
    }
};

\(O(n)\)建树,每个点的权值是由自己的直接儿子得来的于是可以在更新儿子是也更新父亲。

for(int i=1;i<=n;i++){
    int x;
    cin>>x;
    int j=i+(i&-i);
    bit.t[i]+=x;
    if(j<=n)bit.t[j]+=bit.t[i];
}

查询全局第\(k\)小。

倍增,当超过\(n\)或超过\(k\)时减少刚刚加上的\(2\)的某次幂。

inline int kth(int x){
    int re=0,cnt=0;
    for(int i=21;~i;i--){
        re+=(1<<i);
        if(re>=tot||cnt+t[re]>=x)re-=1<<i;
        else cnt+=t[re];
    }
    return re+1;
}

二维树状数组,单点修改区间查询。

struct FenwickTree{
    int t[N][N];
    inline int lowbit(int x){
        return x&-x;
    }
    inline int query(int x,int y){
        int re=0;
        if(x<=0||y<=0)return 0;
        for(int i=x;i;i-=lowbit(i))for(int j=y;j;j-=lowbiy(j))re+=t[i][j];
        return re;
    }
    inline void add(int x,int y,int v){
        for(int i=x<=n;i+=lowbit(i))for(int j=y;j<=m;j+=lowbit(j))t[i][j]+=v;
    }
    inline int ask(int x,int y,int a,int b){
        return query(a,b)+query(x-1,y-1)-query(x-1,b)-query(a,y-1);
    }
};

区间修改区间查询。

使差分数组\(b\)\(a[i][j]\)\(a[i-1][j]+a[i][j-1]-a[i-1][j-1]\)的差,\(\sum_{i=1}^x\sum_{j=i}^y\sum_{h=1}^i\sum_{t=1}^jb[i][j]=\sum_{i=1}^x\sum_{j=1}^y(b[i][j](x-i+1)(y-j+1))\),相当于\(b[i][j]\)\(j*(i-1)+i*(j-1)+(i-1)*(j-1)\)次没有使用,于是维护四个树状数组\(t[i][j]\),\(t[i][j]*(i-1)\),\(t[i][j]*(j-1)\),\(t[i][j]*(i-1)*(j-1)\).

struct FenwickTree{
    int t[N][N][4];
    inline int lowbit(int x){
        return x&-x;
    }
    inline void modify(int x,int y,int v){
        for(int i=x;i<=n;i+=lowbit(i)){
            for(int j=y;j<=m;j+=lowbit(j)){
                t[i][j][0]+=v;
                t[i][j][1]+=v*(x-1);
                t[i][j][2]+=v*(y-1);
                t[i][j][3]+=v*(x-1)*(y-1);
            }
        }
    }
    inline int query(int x,int y){
        int a=0,b=0,c=0,d=0;
        for(int i=x;i;i-=lowbit(i)){
            for(int j=y;j;j-=lowbit(j)){
                a+=t[i][j][0];
                b+=t[i][j][1];
                c+=t[i][j][2];
                d+=t[i][j][3];
            }
        }
        return a*x*y-b*y-c*x+d;
    }
    inline void add(int x,int y,int a,int b,int v){
        modify(x,y,v);
        modify(x,b+1,-v);
        modify(a+1,y,-v);
        modify(a+1,b+1,v);
    }
    inline int ask(int x,int y,int a,int b){
        return query(a,b)+query(x-1,y-1)-query(x-1,b)-query(a,y-1);
    }
};

每次询问一个区间的所有子区间的异或和的异或和。当区间长度为偶数时,每个数都被贡献偶数次,答案肯定为\(0\).当区间长度为奇数时,只有与左端点奇偶性相同的下标才会被贡献,可以分奇数和偶数两个树状数组进行维护。

    inline void add(int x,int v){while(x<=n)t[x]^=v,x+=lowbit(x);}
    inline int query(int x,int re=0){while(x)re^=t[x],x-=lowbit(x);return re;}
    for(int i=1;i<=n;i++){
        cin>>a[i];
        t[i&1].add(i,a[i]);
    }
    while(q--){
        int op,x,y;
        cin>>op>>x>>y;
        if(op==1)t[x&1].add(x,a[x]^y),a[x]=y;
        else{
            if(x+y&1)cout<<"0\n";
            else cout<<(t[x&1].query(y)^t[x&1].query(x-1))<<'\n';
        }
    }

对于一个排列,将每一个连续的递减子串进行翻转,长度不能为\(1\),问最少反转多少次后序列递增。首先按照题意进行翻转,之后每次翻转最多有两个数,于是变成相邻数字的交换,也就是逆序对数。

    int st=1;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(i==1)continue;
        if(a[i]>a[i-1]){
            reverse(a+st,a+i);
            st=i;
            ans++;
        }
    }
    reverse(a+st,a+1+n);
    ans++;

给定一些二元组\((x,w)\)\(x\)递增,每次询问区间\([l,r]\),求\(min(|x[i]-x[j]|*(w[i]+w[j]))(l<=i<j<=r)\)

由于\(x\)递增,可以将绝对值拆开,考虑i什么时候会对答案做出贡献,求出一个\(l[i]\)表示在\(i\)左面第一个比i小的数,\(r[i]\)表示在\(i\)右面第一个比\(i\)小的数答案肯定是\((l[i],i)\)\((i,r[i])\)其中之一。设\(i<j\),当\(w[i]<=w[j]\)时取\(i=l[j]\),当\(w[i]>w[j]\)时取\(j=r[i]\),两边单调栈,将询问离线,扫描右端点。

struct BIT{
    int t[N];
    inline BIT(){memset(t,0x3f,sizeof(t));}
    inline void add(int x,int v){while(x)t[x]=min(t[x],v),x-=x&-x;}
    inline int query(int x,int re=LLONG_MAX){while(x<=n)re=min(re,t[x]),x+=x&-x;return re;}
}bit;
    for(int i=1;i<=n;i++){
        while(s[0]&&w[s[s[0]]]>w[i])s[0]--;
        if(s[0])v[i].push_back(s[s[0]]);
        s[++s[0]]=i;
    }
    s[0]=0;
    for(int i=n;i;i--){
        while(s[0]&&w[s[s[0]]]>w[i])s[0]--;
        if(s[0])v[s[s[0]]].push_back(i);
        s[++s[0]]=i;
    }
    for(int i=1;i<=q;i++){
        int l,r;
        cin>>l>>r;
        p[r].emplace_back(l,i);
    }
    for(int i=1;i<=n;i++){
        for(int j:v[i])bit.add(j,(x[i]-x[j])*(w[i]+w[j]));
        for(auto j:p[i])ans[j.second]=bit.query(j.first);
    }
posted @ 2022-11-14 18:06  半步蒟蒻  阅读(21)  评论(0)    收藏  举报