CodeForces 538F A Heap of Heaps

原题

Andrew skipped lessons on the subject ‘Algorithms and Data Structures’ for the entire term. When he came to the final test, the teacher decided to give him a difficult task as a punishment.

The teacher gave Andrew an array of n numbers a1, …, an. After that he asked Andrew for each k from 1 to n - 1 to build a k-ary heap on the array and count the number of elements for which the property of the minimum-rooted heap is violated, i.e. the value of an element is less than the value of its parent.

Andrew looked up on the Wikipedia that a k-ary heap is a rooted tree with vertices in elements of the array. If the elements of the array are indexed from 1 to n, then the children of element v are elements with indices k(v - 1) + 2, …, kv + 1 (if some of these elements lie outside the borders of the array, the corresponding children are absent). In any k-ary heap every element except for the first one has exactly one parent; for the element 1 the parent is absent (this element is the root of the heap). Denote p(v) as the number of the parent of the element with the number v. Let’s say that for a non-root element v the property of the heap is violated if av < ap(v).

Help Andrew cope with the task!

Input
The first line contains a single integer n (2 ≤ n ≤ 2·105).

The second line contains n space-separated integers a1, …, an ( - 109 ≤ ai ≤ 109).

Output
in a single line print n - 1 integers, separate the consecutive numbers with a single space — the number of elements for which the property of the k-ary heap is violated, for k = 1, 2, …, n - 1.

Examples
inputC
5
1 5 4 3 2
output
3 2 1 0
input
6
2 2 2 2 2 2
output
0 0 0 0 0


题意

给定一个长度为n的数组A,将它变为一颗k叉树(1 <= k <= n - 1)(堆的形式编号)。
问对于每一个k,有多少个节点小于它的父节点。


解题

显然,最初的想法是暴力。因为树的层数到后来好像挺小的。但是仔细看复杂度,好像稳稳的O(n^2)。
那就尴尬了。考虑用数据结构优化。

看其他dalao切此题,用的都是主席树、函数式线段树之类的,但juruo我都没学过怎么办qwq
于是只能好生考虑一番.
我们发现:1、一个节点的儿子在原序列中一定是连续的;2、能对某个节点造成贡献的儿子节点的值一定小于该节点。
于是我们就有如下做法:把节点按值排序,从小到大枚举每个节点。对于k叉数,它儿子中被处理过的节点数,就是这个点对答案中k叉树情况的贡献。
由于2,我们不难得到该做法的正确性。我们再来考虑这个做法的时间复杂度。对于每个点,我们仅考虑它不是树叶的情况。所以实际上对于每个k,我们只考虑了n/k个点。这是O(nlogn)的。然后查询由于结论1,我们可以由树状数组维护,复杂度O(logn)。所以总复杂度O(nlogn^2)。并不会超时。


代码实现

我们将编号从0开始,这样更方便。
我们每次处理好一个节点后就在它原数组的位置上标上1,这样就可一直接用树状数组求和。

#include<bits/stdc++.h>
using namespace std;
struct Node{
    int x, id;
    Node(int x_ = 0, int id_ = 0) {x = x_; id = id_;}
};
const int MAXN = 200010;
int n, x, ans[MAXN], tree[MAXN];
Node a[MAXN];

bool cmp(Node x, Node y){
    return x.x < y.x || x.x == y.x && x.id < y.id;
}

int lowbit(int x){ return x & (-x); }

int ask(int x){
    int t = 0;
    while(x){
        t += tree[x];
        x -= lowbit(x);
    }
    return t;
}

void add(int x){
    if(x == 0) return;
    while(x < n){
        tree[x]++;
        x += lowbit(x);
    }
    return;
}

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        scanf("%d", &x);
        a[i] = Node(x, i);
    }
    sort(a, a + n, cmp);
    memset(ans, 0, sizeof(ans));
    for(int i = 0; i < n; i++){
        for(int k = 1; k * a[i].id + 1 < n && k < n; k++){
            ans[k] += ask(min(k * a[i].id + k, n - 1)) - ask(k * a[i].id);
        }
        add(a[i].id);
    }
    for(int i = 1; i < n; i++) printf("%d ", ans[i]); printf("\n");
    return 0;
}
### 关于 Codeforces Problem 1802A 目前提供的引用内容并未涉及 Codeforces 编号为 1802A 的题目详情或解决方案[^1]。然而,基于常见的竞赛编程问题模式以及可能的解决方法,可以推测该类题目通常围绕算法设计、数据结构应用或者特定技巧展开。 如果假设此题属于典型的算法挑战之一,则可以从以下几个方面入手分析: #### 可能的方向一:字符串处理 许多入门级到中级难度的问题会考察字符串操作能力。例如判断子串是否存在、统计字符频率或是执行某种转换逻辑等。以下是 Python 中实现的一个简单例子用于演示如何高效地比较两个字符串是否相匹配: ```python def are_strings_equal(s1, s2): if len(s1) != len(s2): return False for i in range(len(s1)): if s1[i] != s2[i]: return False return True ``` #### 方向二:数组与列表的操作 另一常见主题是对整数序列进行各种形式上的变换或者是查询最值等问题。下面给出一段 C++ 程序片段来展示快速寻找最大元素位置的方法: ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int n; cin >> n; vector<int> a(n); for(auto &x : a){ cin>>x; } auto max_it = max_element(a.begin(),a.end()); cout << distance(a.begin(),max_it)+1; // 输出索引加一作为答案 } ``` 由于具体描述缺失,在这里仅提供通用框架供参考。对于确切解答还需要访问实际页面获取更多信息后再做进一步探讨[^3]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值