1. 程式人生 > >[CF538F]A Heap of Heaps(主席樹)

[CF538F]A Heap of Heaps(主席樹)

葉子 時間 scan oot .cn push fine i++ 復雜

題面

題意:給你一個數組a[n],對於數組每次建立一個完全k叉樹,對於每個節點,如果父節點的值比這個節點的值大,那麽就是一個違規點,統計出1~n-1完全叉樹下的違規點的各自的個數。

分析

註意到完全k叉樹的一個性質,v節點的兒子是k*(v-1)+2...kv+1,v節點的父親為(v+k-2)/k

那我們可以暴力枚舉k,然後枚舉每個點i,但是我們沒必要枚舉葉子節點,也就是說i的範圍是0到最後一個葉子節點n的父親,即[0,(v+n-2)/k]

然後對於每個點i,在對應的子節點區間裏查詢值在[0,a[i]-1]裏的節點個數。由於主席樹維護的就是1~i中有多少個節點的值落在[l,r]內,直接區間求和然後相減就可以了

由於n個節點的k叉樹最多有\(\frac{n}{k}\)個葉子節點

時間復雜度為\(\sum_{k=1}^{n-1} \frac{n}{k}=O(n\log n)\)

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 200005
#define maxlogn 20
using namespace std;
struct node{
#ifdef DEBUG
    int l;
    int r;
#endif
    int ls;
    int rs;
    int cnt; 
}tree[maxn*maxlogn];
int ptr;
inline void push_up(int x){
    tree[x].cnt=tree[tree[x].ls].cnt+tree[tree[x].rs].cnt;
}
void update(int &x,int last,int upos,int l,int r){
    x=++ptr;
    tree[x]=tree[last];
#ifdef DEBUG
    tree[x].l=l;
    tree[x].r=r;
#endif
    if(l==r){
        tree[x].cnt++;
        return;
    }
    int mid=(l+r)>>1;
    if(upos<=mid) update(tree[x].ls,tree[last].ls,upos,l,mid);
    else update(tree[x].rs,tree[last].rs,upos,mid+1,r);
    push_up(x);
}
int get_sum(int L,int R,int l,int r,int x){
    if(L<=l&&R>=r){
        return tree[x].cnt;
    }
    int mid=(l+r)>>1;
    int ans=0;
    if(L<=mid) ans+=get_sum(L,R,l,mid,tree[x].ls);
    if(R>mid) ans+=get_sum(L,R,mid+1,r,tree[x].rs);
    return ans;
}

int n;
int root[maxn];
int a[maxn];
int b[maxn];
int ans[maxn];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    int m=n;
    sort(b+1,b+1+m);
    m=unique(b+1,b+1+m)-b-1;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+1+m,a[i])-b;
    }
    for(int i=1;i<=n;i++){
        update(root[i],root[i-1],a[i],1,m);
    }
    
    for(int k=1;k<=n-1;k++){
        int lim=(n+k-2)/k;
        for(int i=1;i<=lim;i++){
            int l=k*(i-1)+2;
            int r=min(k*i+1,n);
            int cnt=0;
            if(a[i]-1<1) cnt=0; 
            else cnt=get_sum(1,a[i]-1,1,m,root[r])-get_sum(1,a[i]-1,1,m,root[l-1]);
            ans[k]+=cnt;
        }
    }
    for(int i=1;i<=n-1;i++){
        printf("%d ",ans[i]);
    }
}

[CF538F]A Heap of Heaps(主席樹)