树状数组还不会的可以看:https://blog.csdn.net/WhereIsHeroFrom/article/details/78922383
鉴于博客中个别点说的很简短,有时难以理解,这里再补充一些,帮助理解:
关于IUPQ模型的理解:
IUPQ模型:区间更新,点查询。
切入点:差分数组
设原数组为A[] = a, b, c, d, e。差分数组B[] = a, b-a, c-b, d-c, e-d。
可以看出A[n] = B[1] + B[2] + …+ B[n]。前缀和!
当需要从[l, r]加v时,可以看到,只有原数组[l, r]区间的交界处的差值变了,区间内部的差值并没有改变,所以只更新交界处即可:B[l] + v就代表着A[l] 到 A[n]所有数都 + v,
而在B[r+1]处将[r+1, n]区间的 +v 效果消除,即b[r+1] - v。
还是举个例子,设数组a[]={1,6,8,5,10},那么差分数组b[]={1,5,2,-3,5}
也就是说b[i]=a[i]-a[i-1];(a[0]=0;),那么a[i]=b[1]+…+b[i];(这个很好证的)。
假如区间[2,4]都加上2的话
a数组变为a[]={1,8,10,7,10},b数组变为b={1,7,2,-3,3};
发现了没有,b数组只有b[2]和b[5]变了,因为区间[2,4]是同时加上2的,所以在区间内b[i]-b[i-1]是不变的.
所以对区间[x,y]进行修改,只用修改b[x]与b[y+1]:
b[x]=b[x]+k;b[y+1]=b[y+1]-k;
关于逆序对模型的理解:
问题模型,给定n个整数,求出逆序整数对数(即a[u] > a[v] && u < v的整数对)
看见题目我们就可以写出O(n2)的暴力for来求解,但是如何将其用树状数组来优化到O(nlogn)呢
这里用到桶排序的思想.
首先让问题简单化一点,让n个整数a[i]均小于等于100.
那么我们就可以开tree[105]的数组,每次读入更新一个数时,只需update(a[i],1)
此时,比a[i]大的数字就是需要更新的逆序对对数,即query(a[100]) - query(a[i])因为当前共读入了i个数,那么求值亦可简化为i - query[a[i]]
也就是读入一轮+每次查询就可得到总共的逆序对对数.时间复杂度O(nlogn)。
那么当数据足够大时,我们的数组存不下的时候呢,这时候,就轮到我们的核心思想,离散化出场了.
对于离散化,个人理解就是由于想要利用桶数组,故将数据相对缩小(保证相对大小不变)到可以开到的数组那么大而减小空间需求。还是举个例子,比如一个数组初始为:10, 14523578, 564, 5, 484184164。对这个数组而言,最大的数要484184164,,直接开数组显然是不行的,由于逆序对并不关心具体的值是多少,只关心相对大小,所以我们大可以改变其值,只要保证其相对大小即可,例如总共有5个数,可以依次按照大小顺序,将数组变为2,4,3,1,5。
hdu1166
// 点更新,区间查询——PUIQ模型
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 50000+5;
using namespace std;
int n, C[maxn];
char op0[10] = "End", op1[10] = "Add", op2[10] = "Sub",op3[10] = "Query", str[10];
int lowbit(int x){return x & -x;}
void add(int x, int v){ for(int i = x; i <= maxn; i+= lowbit(i)) C[i]+= v; }
int sum(int x){
int s = 0;
for(int i = x; i > 0; i-= lowbit(i)) s+= C[i];
return s;
}
int main()
{
freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
int kase = 1;
while(T--){
memset(C, 0, sizeof(C));
int x, y;
scanf("%d",&n);
for(int i = 1; i <= n; ++i){ scanf("%d",&x); add(i, x); }
printf("Case %d:\n", kase++);
do{
scanf("%s",str);
if(strcmp(str, op0) == 0) break;
scanf("%d%d",&x,&y);
if(strcmp(str, op1) == 0) add(x, y);
else if(strcmp(str, op2) == 0) add(x, -y);
else if(strcmp(str, op3) == 0){
int s = sum(y) - sum(x-1);
printf("%d\n", s);
}
}while(1);
}
fclose(stdin);
return 0;
}