[CF538F]A Heap of Heaps(主席樹)
阿新 • • 發佈:2019-05-12
葉子 時間 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(主席樹)