【每日一题】Namomo Spring Camp 2022 Div1 #树上逆序对

树上逆序对

题目链接

题意

对于一棵有根树,定义树上的逆序对为满足 a a ai< a a afai 的二元对 ( i , (i, (i,f   a i   ) ~ai~)  ai ) , 其中 f f fai 表示结点 i i i 的父亲结点

对于一棵 k k k 叉树, 结点 i i i 的子节点的编号集合为 [ 1 , n ] ∩ [ k ( i − 1 ) + 2 , k i + 1 ] [1,n]∩[k(i−1)+2,ki+1] [1,n][k(i1)+2,ki+1] 中的所有整数

给定 n n n 个结点的权值 a a a1, a a a2,…, a a an , 对于 k = 1 , 2 , … , n − 1 k=1,2,…,n−1 k=1,2,,n1 求出当这 n n n 个结点构成一棵 k k k 叉树时树上逆序对数

1 ≤ \leq n n n ≤ \leq 2 e 5 2e^5 2e5 , 1 ≤ \leq a a ai ≤ \leq 1 0 9 10^9 109

思路

这里我们先考虑枚举 k k k ,再枚举每个点的所有孩子节点,发现这样的复杂度是 n 2 n^2 n2 , 而且这样是很难优化进去的

然后我们换个思考方向,我们这里先枚举每个点 ,再枚举 k k k , 但这里虽然听起来还是 n 2 n^2 n2 的,但里面复杂度是 O ( a c ) O(ac) O(ac) 的,为啥勒

我们将每个点先按权重从小到大,再按位置从小到大排序,把这些点一个一个插进树状数组里面,计算每个点的对答案的贡献,同时不重不漏

代码

#include <stdio.h>
#include <cstdio>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <cmath>
#include <vector>
#include <algorithm>
#include <set>
#include <iostream>
using namespace std;
const int maxn=2e5+100;

int n;
int tree[maxn << 2];

int read() {
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

struct node{
    int id,val;
    bool operator<(const node& rhs)const {
        if(val!=rhs.val) return val<rhs.val;
        return id<rhs.id;
    }
}a[maxn];
int lowbit(int k) {return k&(-k);}
void add(int x,int k) {
	while (x <= n) {
		tree[x] += k;
		x += lowbit(x);
	}
}
int sum(int x) {
	int ans = 0;
	while (x != 0) {
		ans += tree[x];
		x -= lowbit(x);
	}
	return ans;
}
int ans[maxn];
void solve(){
    n=read();
    for(int i=1;i<=n;++i) a[i].val=read(),a[i].id=i;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<n;j++){
            int x=(j)*(a[i].id-1)+2;
            int y=min(j*a[i].id+1,n);
            if(x>n) break;
            ans[j]+=sum(y)-sum(x-1);
        }
        add(a[i].id,1);
    }
    for(int j=1;j<n;++j){
        write(ans[j]);
        printf(" ");
    }
}
int main() {
    solve();
	return 0;
}
/*
*/



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值