题意:给你一个数组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; }