现在有一个长度为n的数组 a1, a2, ..., an 。然后对于k从1到 n-1分别对该数组建k叉堆。现在要统计对于每一个k叉堆,里面有多少结点是不满足最小堆的性质的。即值比父亲的要小的结点有多少个。
k叉堆的定义是这样的:数组的下标从1到n编号,对于某一个编号为v的结点,他的k个儿子编号是 k(v − 1) + 2, ..., kv + 1 (如果其中某些编号超出n,那些编号就不要)。在k叉堆中,除了根以外,每一个结点都有一个父亲。p(v)表示结点v的父亲。那么一个非根结点不满足最小堆性质当且仅当 av < ap(v)
样例解释:
下面四个图分别对应k=1,2,3,4的情况。
非法结点用红色标记。
Input
单组测试数据。
第一行有一个整数 n (2≤n≤2*10^5)。
第二行有n个用空格分开的整数a1, ..., an (-10^9≤ai≤10^9)。
Output
输出n-1个数字,第k个数字代表k叉堆中不满足最小堆性质的结点数目 (k=1, 2, ..., n-1)。
Input示例
样例输入1
5
1 5 4 3 2
Output示例
样例输出1
3 2 1 0
思路:
此题主要是找出某点的儿子区间中比它小的值。因此将数组的值按照递增排序,然后逐个遍历,当遍历到某个值时,将该值所在位置之前的出现过的其儿子区间的数量相加。遍历完某个值后,按照树状数组规则将该值所在位置之后的数量加一。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct Node
{
int val;
int pos;
};
const int MAXN = 2e5 + 10;
int n;
int tree[MAXN];
int result[MAXN];
Node a[MAXN];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int d)
{
while (x < MAXN)
{
tree[x] += d;
x += lowbit(x);
}
}
int sum(int x)
{
int ans = 0;
while (x)
{
ans += tree[x];
x -= lowbit(x);
}
return ans;
}
void solve ()
{
memset(tree, 0, sizeof (tree));
memset(result, 0, sizeof (result));
for (int i = 1; i <= n; )
{
int tmp = i;
while (tmp <= n && a[tmp].val == a[i].val)
{
tmp++;
}
for (int j = i; j < tmp; j++)
{
int v = a[j].pos;
for (int k = 1; k <= n - 1 && k * (v - 1) + 2 <= n; k++)
{
result[k] += sum(min(n, k * v + 1)) - sum(k * (v - 1) + 1);
}
}
for (int j = i; j < tmp; j++)
{
add(a[j].pos, 1);
}
i = tmp;
}
}
bool cmp(const Node &left, const Node &right)
{
return left.val < right.val;
}
int main(void)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].val;
a[i].pos = i;
}
sort(a + 1, a + n + 1, cmp);
solve();
for (int i = 1; i < n - 1; i++)
{
cout << result[i] << " ";
}
cout << result[n - 1] << endl;
return 0;
}