求区间中不同的数的个数(离线 线段树)
HH的项链
思路:
离线按照r排序,然后把小于r的数字加到线段树中,同时把出现过的a[j]的位置在线段树中删掉
代码:
#define read() FastIO::read()
#define clean() FastIO::flush()
const int N = 1000010;
int a[N];
int tr[N << 2];
int M = 1;
void pushup(int p) {
tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
void build(int n) {
for (M = 1; M <= n + 5; M <<= 1);
//for(int i=i;i<=n;i++) tr[i+M]=a[i];
for (int i = M; i; i--) {
pushup(i);
}
}
void modify(int p, int v) {
p = p + M;
tr[p] = v;
for (p >>= 1; p; p >>= 1) pushup(p);
}
int query(int l, int r) {
int ans = 0;
for (l += M - 1, r += M + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ans += tr[l ^ 1];
if (r & 1) ans += tr[r ^ 1];
}
return ans;
}
struct T {
int l, r, id;
bool operator<(const T &t)const {
return r < t.r;
}
} q[N];
int ans[N];
int vis[N];
void solve(int Case) {
int n = read();
for (int i = 1; i <= n; i++) a[i] = read();
int m = read();
build(n);
for (int i = 1; i <= m; i++) {
int l = read(), r = read();
q[i] = {l, r, i};
}
sort(q + 1, q + 1 + m);
for (int i = 1, j = 1; i <= m; i++) {
auto [l, r, id] = q[i];
for (; j <= r; j++) {
if (vis[a[j]]) {
int p = vis[a[j]];
modify(p, 0);
modify(j, 1);
vis[a[j]] = j;
} else {
modify(j, 1);
vis[a[j]] = j;
}
}
ans[id] = query(l, r);
}
for (int i = 1; i <= m; i++) {
printf("%lld\n", ans[i]);
}
}
signed main() {
solve(Case);
return 0;
}
[HEOI2012]采花
思路:
题意是要求l到r中出现次数超过1次的所有颜色数量,所以只需要对每种颜色维护最近的两个位置(x,y),维护线段树并且把权值加在x上面,按照上题思路更新即可
要点:每次维护两个位置可以保证出现次数,把权值只能加在x这个点上面,加到y上面如果查询的左端点大于x且小于y就会出现错误
代码
const int N = 2000010;
int a[N];
int tr[N << 2];
int M = 1;
void pushup(int p) {
tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
void build(int n) {
for (M = 1; M <= n + 5; M <<= 1);
//for(int i=i;i<=n;i++) tr[i+M]=a[i];
for (int i = M; i; i--) {
pushup(i);
}
}
void modify(int p, int v) {
p = p + M;
tr[p] += v;
for (p >>= 1; p; p >>= 1) pushup(p);
}
int query(int l, int r) {
int ans = 0;
for (l += M - 1, r += M + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ans += tr[l ^ 1];
if (r & 1) ans += tr[r ^ 1];
}
return ans;
}
struct T {
int l, r, id;
bool operator<(const T &t)const {
return r < t.r;
}
} q[N];
int ans[N];
pair<int, int> vis[N];
void solve(int Case) {
int n = read(), c = read(), m = read();
for (int i = 1; i <= n; i++) a[i] = read();
build(n);
for (int i = 1; i <= m; i++) {
int l = read(), r = read();
q[i] = {l, r, i};
}
sort(q + 1, q + 1 + m);
for (int i = 1, j = 1; i <= m; i++) {
auto [l, r, id] = q[i];
for (; j <= r; j++) {
auto &[x, y] = vis[a[j]];
if (x > y) swap(x, y);
if (!x or !y) {
if (!x) {
x = j;
} else if (!y) {
y = j;
}
if (x and y) {
if (x > y) swap(x, y);
modify(x, 1);
}
} else {
int t = min(x, y);
modify(t, -1);
if (x > y) swap(x, y);
x = j;
swap(x, y);
modify(x, 1);
}
if (x > y) swap(x, y);
}
int d = query(l, r);
ans[id] = d ;
}
for (int i = 1; i <= m; i++) {
printf("%lld\n", ans[i]);
}
}
signed main() {
solve(Case);
return 0;
}
如果说求出现在l到r之间至少k个的数量,就维护k个,更新最小的k权值为1