Codeforces 538F【可持久化线段树】

注意到两点:

1.n个节点的完全K叉树中,有儿子的节点数量是O(n/k).而Hn是O(nlogn)的.

2.每个节点的儿子是一段连续的区间,因此我们就可以用可持久化线段树来高效查找.

/* I will wait for you*/

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <string>
#define make make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int maxn = 300010;
const int maxm = 1010;
const int maxs = 26;
const int inf = 0x3f3f3f3f;
const int P = 1000000007;
const double error = 1e-9;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while (ch <= 47 || ch >= 58)
		f = (ch == 45 ? -1 : 1), ch = getchar();
	while (ch >= 48 && ch <= 57)
		x = x * 10 + ch - 48, ch = getchar();
	return x * f;
}

struct node
{
	int l, r, size;
	node *ls, *rs;

	node(int a, int b) {
		l = a, r = b, size = 0;
		ls = rs = 0;
	}
} *su[maxn];

int n, u, v, ans[maxn], num[maxn], w[maxn];

map<int, int> now;

node* perbuild(int l, int r)
{
	node* o = new node(l, r);

	if (l == r)
		return o;

	int mid = (l + r) / 2;

	o -> ls = perbuild(l, mid);
	o -> rs = perbuild(mid + 1, r);
	
	return o;
}

node* insert(node* p, int w)
{
	int l = p -> l, r = p -> r;

	node *o = new node(l, r);

	o -> size = p -> size + 1;
	
	o -> ls = p -> ls, o -> rs = p -> rs;

	if (l == r)
		return o;

	int mid = (l + r) / 2;

	if (w <= mid)
		o -> ls = insert(p -> ls, w);
	if (w > mid)
		o -> rs = insert(p -> rs, w);

	return o;
}

int query(node *a, node *b)
{
	int l = a -> l, r = b -> r;

	if (l >= u && r <= v)
		return b -> size - a -> size;

	int mid = (l + r) / 2, ans = 0;

	if (u <= mid)
		ans += query(a -> ls, b -> ls);
	if (v > mid)
		ans += query(a -> rs, b -> rs);

	return ans;
}

int main()
{
	n = read();
	
	su[0] = perbuild(1, n);

	for (int i = 1; i <= n; i++)
		w[i] = num[i] = read();

	sort(w + 1, w + 1 + n);

	for (int i = 1; i <= n; i++)
		now[w[i]] = i;

	for (int i = 1; i <= n; i++) {
		num[i] = now[num[i]];
		su[i] = insert(su[i - 1], num[i]);
	}

	for (int i = 1; i < n; i++) {
		for (int j = 1; j <= n; j++) {
			int l = i * (j - 1) + 2;
			int r = i * j + 1;
			
			if (l > n) 
				break;
			if (r > n) 
				r = n;
			if (num[j] == 1)
				continue;

			u = 1, v = num[j] - 1;

			ans[i] += query(su[l - 1], su[r]);
		}
		printf("%d ", ans[i]);
	}

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值