• [CF538F]A Heap of Heaps(主席树)


    题面

    题意:给你一个数组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(nlog 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]);
    	}
    }
    
    
  • 相关阅读:
    Array数组的使用
    map集合中取出分类优先级最高的类别名称
    关键字static介绍
    构造方法
    封装和private,this,super关键字的简单应用
    hitTest:withEvent:方法流程
    Events and Responder Chain
    block没那么难(一):block的实现
    [译] Block 小测验
    OAuth流程
  • 原文地址:https://www.cnblogs.com/birchtree/p/10851793.html
Copyright © 2020-2023  润新知