zoukankan      html  css  js  c++  java
  • Codeforces538F A Heap of Heaps(函数式线段树)

    题意:给你一个数组a[n],对于数组每次建立一个完全k叉树,对于每个节点,如果父节点的值比这个节点的值大,那么就是一个违规点,统计出1~n-1完全叉树下的违规点的各自的个数。

    一个直觉的思想就是暴力,因为完全k叉树当k很大的时候,其实层数是特别小的,所以感觉暴力是可以的。注意到一个完全k叉树下v节点的儿子的公式是:

    k*(v-1)+2...kv+1,相应的父节点的公式是 (v+k-2)/k。儿子的编号是连续的,如果我们可以对每个节点快速的求出连续编号的节点有多少个数比它小我们就可以快速的更新答案了,但是如果对每个节点都这样做的话就至少是一个O(n^2)级别的做法。注意到对于一棵完全k叉树来说,只有内节点才需要统计,叶节点并不需要。而对于一个大小为n的完全k叉树来说,内节点的个数是O(n/k)的,因此总的内节点个数就是n/1+n/2+n/3+...n/n-1,即O(nlogn)。

    然后就是单次询问一段连续的区间里有多少个数比v小。这里我没有想到什么好的简便的方法,不过函数式线段树是一个解决方法。root[i]表示的是用a[i]~a[n]的值建立的线段树,当我需要询问某个区间[l,r]的小于等于v的数有多少个数时,只需要query(root[l],1,v)-query(root[r],1,v)即可。空间复杂度是O(nlogn),时间复杂度是单次询问O(logn),最后总的复杂度就是O(nlog^2 n)

    #pragma warning(disable:4996)
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <cmath>
    #include <string>
    #include <vector>
    #include <queue>
    using namespace std;
    
    #define maxn 200500
    #define maxc maxn*20
    
    int n;
    int a[maxn], b[maxn];
    int res[maxn];
    int root[maxn];
    int nsize;
    
    int lc[maxc], rc[maxc];
    int sum[maxc];
    int tot;
    
    int insert(int rt, int L, int R, int v)
    {
    	int cur = tot++;
    	if (L == R){
    		sum[cur] = sum[rt] + 1;
    		return cur;
    	}
    	int M = (L + R) >> 1;
    	if (v <= M){
    		rc[cur] = rc[rt];
    		lc[cur] = insert(lc[rt], L, M, v);
    	}
    	else{
    		lc[cur] = lc[rt];
    		rc[cur] = insert(rc[rt], M + 1, R, v);
    	}
    	sum[cur] = sum[lc[cur]] + sum[rc[cur]];
    	return cur;
    }
    
    
    int query(int rt, int L, int R, int l, int r)
    {
    	if (l == L&&r == R){
    		return sum[rt];
    	}
    	int M = (L + R) >> 1;
    	if (r <= M){
    		return query(lc[rt], L, M, l, r);
    	}
    	else if (l>M){
    		return query(rc[rt], M + 1, R, l, r);
    	}
    	else{
    		return query(lc[rt], L, M, l, M) + query(rc[rt], M + 1, R, M + 1, r);
    	}
    }
    
    
    int main()
    {
    	while (cin >> n)
    	{
    		for (int i = 1; i <= n; ++i) {
    			scanf("%d", a + i);
    			b[i] = a[i];
    		}
    		sort(b+1, b + n+1);
    		nsize = unique(b+1, b + n+1) - b;
    		for (int i = 1; i <= n; ++i){
    			a[i] = lower_bound(b + 1, b + nsize, a[i]) - b + 1;
    		}
    		memset(res, 0, sizeof(res));
    		tot = 1;
    		root[n + 1] = tot;
    		lc[tot] = rc[tot] = sum[tot] = 0;
    		tot++;
    		for (int i = n; i >= 1; i--){
    			root[i] = insert(root[i + 1], 1, nsize, a[i]);
    		}
    		for (int k = 1; k <= n - 1; ++k){
    			int maxBound = (n + k - 2) / k;
    			for (int v = 1; v <= maxBound; ++v){
    				int cnt = 0;
    				int lbound = k*(v - 1) + 2;
    				int rbound = min(k*v + 1, n);
    				cnt = query(root[lbound], 1, nsize, 1, a[v] - 1)- query(root[rbound+1], 1, nsize, 1, a[v] - 1);
    				res[k] += cnt;
    			}
    		}
    		for (int i = 1; i <= n - 1; ++i){
    			if (i > 1) printf(" ");
    			printf("%d", res[i]);
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    CSS 中的字体兼容写法:用CSS为英文和中文字体分别设置不同的字体
    利用vue-cropper做的关于图片裁剪、压缩、上传、预览等做的一个公共组件
    解决浏览器拦截弹出窗口问题
    详解Vue中的nextTick
    vue里ref ($refs)用法
    vue组件的hover事件模拟、给第三方组件绑定事件不生效问题
    JS实现千分位
    JS实现异步编程的4种方法
    Cas_个人理解
    zabbix_监控_邮件预警
  • 原文地址:https://www.cnblogs.com/chanme/p/4464506.html
Copyright © 2011-2022 走看看