【算法】【树状数组】

20 阅读2分钟

基础用法

逆序对 atcoder.jp/contests/ab…

#include <bits/stdc++.h>  
using namespace std;  
using ll=unsigned long long;  
template<typename T>  
struct ft{  
    int n;  
    std::vector<T>data;  
    ft(int n_):n(n_),data(n_+1,0){}  
    void add(int idx,T de){  
        for(++idx;idx<=n;idx+=idx&-idx){  
            data[idx]+=de;  
        }  
    }  
    sum(int idx){  
        T res=0;  
        for(;idx>0;idx-=idx&-idx){res+=data[idx];}return res;  
    }sum(int l,int r){return sum(r)-sum(l);}  
};  
void so(){  
      
}int main(){  
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);  
    int n;ll k;  
    cin>>n>>k;  
    vector<int>p(n);  
    for(int i=0;i<n;i++){  
        cin>>p[i];p[i]--;  
    }auto solve=[&](ll k)->ll{  
        ft<ll>bit(n);  
        ll now=0;ll ans=0;  
        int r=0;  
        for(int l=0;l<n;l++){  
            if(r<l)r=l;  
            while(r<n&&now+bit.sum(p[r],n)<=k){  
                //当前以存在的数中,比p[r]大的数的数量  
                now+=bit.sum(p[r],n);  
                bit.add(p[r],1);  
                r++;  
            }ans+=r-l;  
            now-=bit.sum(0,p[l]);//减少的逆序对数是比p[r]小的数的数量  
            bit.add(p[l],-1);//树状数组中剔除  
        }return ans;  
    };ll ans=solve(k)-(k>0?solve(k-1):0);  
    cout<<ans<<'\n';  
}

E - LEQ

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod=998244353;
struct bt{
	int N;
	vector<ll>bit;
	bt(int n):N(n){bit.resize(N+1,0);
	
	}ll ad(ll x,ll y){
		return (x+y)%mod;
	}void add(int x,ll a){
		x++;
		for(x;x<=N;x+=(x&-x))bit[x]=ad(bit[x],a);
	}ll sum(int x){
		x++;
		ll ret=0;
		for(x;x>0;x-=(x&-x))ret=ad(ret,bit[x]);
		return ret;
	}
	
};
ll modpow(ll x, ll y){
    ll ret = 1;
    while(0 < y){
        if(y & 1){
            ret *= x;
            ret %= mod;
        }
        x *= x;
        x %= mod;
        y >>= 1;
    }
    return ret;
}
int comp(vector<int>&a){
	map<int,int>me;
	for(auto p:a)me[p]=0;
	int sz=0;
	for(auto &p:me)p.second=sz++;
	for(auto &p:a)p=me[p];
	return sz;
}
int main(){
	const ll div=modpow(2,mod-2);
	int N;cin>>N;
	vector<int>a(N);
	for(int i=0;i<N;i++)cin>>a[i]; 
	int n=comp(a);
	bt bit(n);
	ll ans=0;
	for(int i=0;i<N;i++){
		ans+=bit.sum(a[i])*modpow(2,i);
		ans%=mod;
		bit.add(a[i],modpow(div,i+1));
	}
	cout<<ans;
}

思路

图片.png -------------------- 核心公式 1 --------------------

bit.sum(a[j]) = 所有 i<j 且 A[i] ≤ A[j] 的 (1/2^(i+1)) 之和

乘上 2^j 就等于题解中的 B_j × 2^(j-1)

对应题解:ans += B_j × 2^(j-1)

ans = (ans + bit.sum(a[j]) * modpow(2, j)) % mod;

-------------------- 核心公式 2 --------------------

把当前位置 j 加入树状数组

存入的值:1 / 2^(j+1)

对应题解:维护 1/2^i

bit.add(a[j], modpow(div, j + 1));

因为题目需要: 对每个 j,求左边 i<j 且 A[i]≤A[j] 的权值和, 还要动态插入数值

这种**“前缀查询 + 单点更新 + 按值大小统计”**的操作, 树状数组是最优选择,复杂度 (O(N\log N)) 能过 3e5。