树状数组学习笔记
一种可以单点修改,查询前缀和的数据结构。
每次操作O(logn)
void update(int x,int k) {
for(;x <= n;x += x & (-x)) t[x]+=k;
}
int query(int x) {
int res=0;
for(;x;x -= x & (-x)) res+=t[x];
return res;
}
思路:离散化数组,排序后a[i].id,b[i].id表示大小第i位的数在原数组的位置。
q[a[i].id] = b[i].id表示a数组排名第i位的数的原位置对应b数组排名第i位的数的原位置,当q[i] = i时即为答案。故求q数组的逆序对即可(树状数组)
AC代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e8 - 3;
const int N = 1e5 + 10;
struct node{
int id,val;
}a[N],b[N];
int ans,n,g[N << 2],q[N];
bool cmp(node x,node y) {
if(x.val == y.val) return x.id < y.id;
return x.val < y.val;
}
void add(int i,int x){
for(;i <= n;i += i & (-i)) g[i] += x;
}
int getsum(int i){
int res = 0;
for(;i;i -= i & (-i)) res += g[i];
return res;
}
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i].val;
a[i].id = i;
}
for(int i = 1;i <= n;i++){
cin >> b[i].val;
b[i].id = i;
}
sort(a + 1,a + 1 + n,cmp);
sort(b + 1,b + 1 + n,cmp);
for(int i = 1;i <= n;i++){
q[a[i].id] = b[i].id;
}
for(int i = 1;i <= n;i++){
add(q[i],1);
ans += i - getsum(q[i]);
ans %= MOD;
}
cout << ans;
return 0;
}
例题二:P3605 [USACO17JAN] Promotion Counting P
思路:跟上一题基本类似,离散化找逆序对即可,只不过变成了在树上
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n,q[N],head[N << 1],cnt,ans[N],g[N];
struct node{
int id,val;
}a[N];
bool cmp(node x,node y){
return x.val < y.val;
}
struct edge{
int to,next;
}e[N << 1];
void add_edge(int u,int v){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void add(int i,int x){
for(;i <= n;i += i & (-i)) g[i] += x;
}
int query(int i){
int res = 0;
for(;i;i -= i & (-i)) res += g[i];
return res;
}
void dfs(int u){
ans[u] -= query(n) - query(q[u]);
for(int i = head[u];i;i = e[i].next){
dfs(e[i].to);
}
ans[u] += query(n) - query(q[u]);
add(q[u],1);
}
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i].val;
a[i].id = i;
}
sort(a + 1,a + n + 1,cmp);
for(int i = 1;i <= n;i++){
q[a[i].id] = i;
}
for(int i = 2;i <= n;i++){
int x;
cin >> x;
add_edge(x,i);
}
dfs(1);
for(int i = 1;i <= n;i++) cout << ans[i] << endl;
return 0;
}
思路:离线树状数组,考虑当我们碰到一个元素如果已经出现过了,那么就将之前出现的那个位置用数状数组-1,否则+1即可
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n,m,a[N],pre[N],g[N << 2],ans[N];
struct node{
int l,r,id;
}q[N];
bool cmp(node x,node y){
return x.r < y.r;
}
void add(int i,int x){
for(;i <= n;i += i & (-i)) g[i] += x;
}
int query(int i){
int res = 0;
for(;i;i -= i & (-i)) res += g[i];
return res;
}
int main(){
cin >> n;
for(int i = 1;i <= n;i++) cin >> a[i];
cin >> m;
for(int i = 1;i <= m;i++){
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
sort(q + 1,q + m + 1,cmp);
for(int i = 1,k = 1;i <= m;i++){
for(int j = k;j <= q[i].r;j++){
if(pre[a[j]]) add(pre[a[j]],-1);
pre[a[j]] = j;
add(j,1);
}
k = q[i].r + 1;
ans[q[i].id] = query(q[i].r) - query(q[i].l - 1);
}
for(int i = 1;i <= m;i++){
cout << ans[i] << endl;
}
return 0;
}
思路:和上一题一样,只是加了一个限制条件
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int n,c,m,a[N],pre[3][N],g[N << 2],ans[N];
struct node{
int l,r,id;
}f[N];
bool cmp(node x,node y){
return x.r < y.r;
}
void add(int i,int x){
for(;i <= n;i += i & (-i)) g[i] += x;
}
int query(int i){
int res = 0;
for(;i;i -= i & (-i)) res += g[i];
return res;
}
int main(){
cin >> n >> c >> m;
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
for(int i = 1;i <= m;i++){
scanf("%d%d",&f[i].l,&f[i].r);
f[i].id=i;
}
sort(f + 1,f + m + 1,cmp);
for(int i = 1,k = 1;i <= m;i++){
for(int j = k;j <= f[i].r;j++){
if(pre[1][a[j]]){
if(pre[2][a[j]]){
add(pre[2][a[j]],-1);
}
add(pre[1][a[j]],1);
}
pre[2][a[j]] = pre[1][a[j]];
pre[1][a[j]] = j;
}
k = f[i].r + 1;
ans[f[i].id] = query(f[i].r) - query(f[i].l - 1);
}
for(int i = 1;i <= m;i++) cout << ans[i] << endl;
return 0;
}
思路:二维树状数组跟二维前缀和差不多
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int n,m,g[N][N][110],a[N][N],q;
void add(int x,int y,int k,int c){
for(int i = x;i <= n;i += i & (-i)){
for(int j = y;j <= m;j += j & (-j)){
g[i][j][c] += k;
}
}
}
int query(int x,int y,int c){
int res = 0;
for(int i = x;i;i -= i & (-i)){
for(int j = y;j;j -= j & (-j)){
res += g[i][j][c];
}
}
return res;
}
int main(){
cin >> n >> m;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
int c;
cin >> c;
a[i][j] = c;
add(i,j,1,c);
}
}
cin >> q;
while(q--){
int op;
cin >> op;
if(op == 1){
int x,y,c;
cin >> x >> y >> c;
add(x,y,-1,a[x][y]);
a[x][y] = c;
add(x,y,1,c);
}
else{
int x,y,p,q,c;
cin >> x >> p >> y >> q >> c;
cout << query(p,q,c) - query(x - 1,q,c) - query(p,y - 1,c) + query(x - 1,y - 1,c) << endl;
}
}
return 0;
}