51nod - 1533 一堆的堆

现在有一个长度为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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值