Hdu1166——树状数组模板题

树状数组还不会的可以看: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;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值