本质上是维护一个表,每次改变一个元素的值,然后求前i个元素的和。
法一:线段树
我本来是想每个叶节点表示从1到该节点位置的元素和,插入一个元素,那么它后面的节点的值都要加一,这便是操作区间。可是我这样是复杂化了,无限WA。
其实就是简单的求和问题,每个节点维护一个sum值就好了。
代码:454ms
#include <cstdio>
#include <cstring>
using namespace std;
#define N 15009
#define L 32010
struct node{
int l, r;
int val;
}Tree[L * 5];
///
int n;
int level[N];
void build(int l, int r, int x)
{
Tree[x].l = l;
Tree[x].r = r;
Tree[x].val = 0;
if (l == r)return;
int mid = (l + r) / 2;
build(l, mid, x * 2);
build(mid + 1, r, x * 2 + 1);
}
void update(int nu, int x)
{
if (Tree[x].l <= nu && nu <= Tree[x].r)
{
Tree[x].val++;
}
if (Tree[x].l == Tree[x].r)return;
int mid = (Tree[x].l + Tree[x].r) / 2;
if (mid < nu)update(nu, x * 2 + 1);
else if (mid >= nu)update(nu, x * 2);
}
int find(int l, int r, int x)
{
if (Tree[x].l == l && Tree[x].r == r)
{
return Tree[x].val;
}
int mid = (Tree[x].l + Tree[x].r) / 2;
if (mid < l)return find(l, r, x * 2 + 1);
if (mid >= r)return find(l, r, x * 2);
return find(l, mid, x * 2) + find(mid + 1, r, x * 2 + 1);
}
int main()
{
// FILE* fp = fopen("in.txt", "r");
scanf( "%d", &n);
build(0, L, 1);
for (int i = 0; i < n; i++) level[i] = 0;
int x, y;
for (int i = 0; i < n; i++)
{
scanf( "%d %d", &x, &y);
level[find(0, x, 1)]++;
update(x, 1);
}
for (int i = 0; i < n; i++)
printf("%d\n", level[i]);
// getchar();
return 0;
}
法二:树状数组
树状数组应用范围较窄,“改变一个元素,求前i个元素的和”。
391ms
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define N 32010
int n, c[N], level[N];
inline int lowbit(int x)
{
return x & (-x);
}
int sum(int x)
{
int ans = 0;
while (x > 0)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
void update(int i)
{
while (i <= N)
{
c[i] ++;
i += lowbit(i);
}
}
int main()
{
int x, y;
scanf("%d", &n);
memset(c, 0, sizeof(c));
memset(level, 0, sizeof(level));
for (int i = 0; i < n; i++)
{
scanf("%d %d", &x, &y);
level[sum(x + 1)]++;
update(x + 1);
}
for (int i = 0; i < n; i++)printf("%d\n", level[i]);
// scanf("%d", &n);
return 0;
}