树状数组-赏花(nkoj2387)
题意分析
询问[l,r]元素种类数
树状数组(带标记)
带标记的树状数组
- 提问可以离线处理最后输出(如果强制在线那又要怎么做?)
- 把颜色转化为0/1标记(记录下next和first/last,然后每个fisrt对应的值置1,l移动的时候就把l对应的地方置0,next[l]对应的值置1)
- 然后求前缀和(树状数组)即是种类数
代码
//
// Created by rv on 2018/4/23.
//
#include <stdio.h>
#include <string.h>
const int BUFFER_SIZE = 10 << 20;
char buffer[BUFFER_SIZE];
char* ptr = buffer;
const int N = 100000 + 5;
const int M = 200000 + 5;
int c[N], next[N], first[N], qlast[N], qnext[M], R[M], ans[M];
struct BIT {
int n;
int* b;
// TODO: private的拷贝构造
BIT(int _n) {
b = new int[_n];
n = _n;
memset(b, 0, n * sizeof(int));
}
void add(int pos, int delta) {
for (int i = pos; i <= n; i += i & -i) {
b[i] += delta;
}
}
int sum(int pos) {
int res = 0;
for (int i = pos; i >= 1; i -= i & -i) {
res += b[i];
}
return res;
}
};
inline void read_int(int& x) {
x = 0;
while (*ptr < '0' || *ptr > '9') {
ptr++;
}
while (*ptr >= '0' && *ptr <= '9') {
x = x * 10 + (*ptr - '0');
ptr++;
}
}
int main() {
fread(buffer, 1, BUFFER_SIZE, stdin);
int n, m, l, r, C;
read_int(n);
BIT bit(n + 1);
C = -1;
for (int i = 1; i <= n; i++) {
read_int(c[i]);
C = c[i] > C ? c[i] : C;
}
read_int(m);
for (int i = 1; i <= m; i++) {
read_int(l);
read_int(r);
qnext[i] = qlast[l];
qlast[l] = i;
R[i] = r;
}
for (int i = n; i >= 1; i--) {
next[i] = first[c[i]];
first[c[i]] = i;
}
for (int i = 1; i <= C; i++) {
if (first[i] > 0) {
bit.add(first[i], 1);
}
}
// for (int i = 1; i <= m; i++) {
// printf("%d\n", R[i]);
// }
for (l = 1; l <= n; l++) {
for (int i = qlast[l]; i >= 1; i = qnext[i]) {
ans[i] = bit.sum(R[i]);
}
bit.add(l, -1);
if (next[l] > 0) {
bit.add(next[l], 1);
}
}
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
PS
坏习惯:
每次都忘记赋初值,每次都没有处理边界情况,写的每一个字符都没有考虑,范围没有搞清楚就写下去了,小问题很多(以后要做到写完每一句话就把这句话静态调试一遍)
运行时间0ms原理:
使用stdio.h和fread读入即可大幅度提升速度,然后就几乎测不出运行时间(Windows的最小运行时间片是15ms)
fread读入优化:
fread(readBuffer, 1, MAX_BUFFER_SIZE, stdin);