初见安~这里是传送门:洛谷P3939 数颜色
题解
题意很显然,操作交换相邻的两个兔子和求区间内某颜色兔子的个数。
区间条件+颜色条件,带修主席树可以一发带走。【可是我不会】
考虑每个询问只跟颜色有关,所以对每个颜色建一棵线段树,表示区间内为该颜色的兔子有多少个。这就很好维护了。对于修改操作,修改a[x]和a[x+1]的信息即可,总共4次;查询操作直接查询x颜色下的线段树。
空间问题的话线段树动态开点即可。
上代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 600005
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
struct TREE {int l, r, sum;} t[maxn << 5];
int n, m, a[maxn], tot;
void add(int p, int l, int r, int x, int d) {
t[p].sum += d; if(l == r) return;
register int mid = l + r >> 1;
if(x <= mid) {
if(!t[p].l) t[p].l = ++tot; //动态开点
add(t[p].l, l, mid, x, d);
}
else {
if(!t[p].r) t[p].r = ++tot;
add(t[p].r, mid + 1, r, x, d);
}
}
int ask(int p, int l, int r, int ls, int rs) {
if(ls <= l && r <= rs) return t[p].sum;
register int mid = l + r >> 1, ans = 0;
if(ls <= mid && t[p].l) ans += ask(t[p].l, l, mid, ls, rs);
if(rs > mid && t[p].r) ans += ask(t[p].r, mid + 1, r, ls, rs);
return ans;
}
signed main() {
// freopen("color.out", "w", stdout);
n = read(), m = read(); tot = 3e5;
for(int i = 1; i <= n; i++) a[i] = read(), add(a[i], 1, n, i, 1);
for(int i = 1; i <= m; i++) {
register int op = read(), l, r, x;
if(op == 1) {
l = read(), r = read(), x = read();
printf("%d\n", ask(x, 1, n, l, r));
}
else {
x = read();
add(a[x], 1, n, x, -1), add(a[x], 1, n, x + 1, 1);//换颜色
add(a[x + 1], 1, n, x + 1, -1), add(a[x + 1], 1, n, x, 1);
swap(a[x], a[x + 1]);
}
}
return 0;
}
迎评:)
——End——