[qbzt寒假]线段树和树状数组

树状数组

(lowbit(x)=x&(-x))

二维树状数组

修改某个点,查询(1,1)到(n,m)的前缀和(树状数组要从1开始)

HDU2642 Stars

(YFF)是个浪漫的人,他喜欢数天上的星星。

为了解决这个问题,我们考虑到天空是一个二维平面,有时星星会很亮,有时星星会很暗。首先,天空中没有明亮的星星,然后一些信息会被给出为“(B) (x) (y)”,其中“(B)”表示明亮,(x)表示(x)坐标,(y)表示在((x,y))星星是明亮的,而“(D) (x) (y)”中的“(D)”表示((x,y))处的恒星是暗淡的。当查询到“(Q) (X1) (X2) (Y1)(Y2)”时,您应该告诉(YFF)该区域有多少明亮的恒星对应于(X1)(X2)(Y1)(Y2)这个四边形区域。

(X,Y (0 <=X,Y<= 1000))

二维树状数组模板

#include<cstdio>
#include<algorithm>
#define N 1010
using namespace std;

int c[N][N];
int f[N][N];
int m;

int lowbit(int x){
	return x&(-x);
}

void modify(int x,int y,int z){
	for(int i=x;i<N;i+=lowbit(i)){
		for(int j=y;j<N;j+=lowbit(j)){
			c[i][j]+=z;
		}
	}
}

int query(int x,int y){
	int ret=0;
	for(int i=x;i;i-=lowbit(i)){
		for(int j=y;j;j-=lowbit(j)){
			ret+=c[i][j];
		}
	}
	return ret;
}

int main(){
	scanf("%d",&m);
	while(m--){
		char opt[10];
		int x,y,a,b;
		scanf("%s",opt);
		if(opt[0]=='B'){
			scanf("%d%d",&x,&y);
			x++;y++;
			if(f[x][y]) continue;
			f[x][y]=1;
			modify(x,y,1);
		}
		if(opt[0]=='D'){
			scanf("%d%d",&x,&y);
			x++;y++;
			if(f[x][y]==0) continue;
			f[x][y]=0;
			modify(x,y,-1);
		}
		if(opt[0]=='Q'){
			scanf("%d%d%d%d",&x,&a,&y,&b);
			x++;y++;a++;b++;
			if(x<a) swap(x,a);
			if(y<b) swap(y,b);
			int ans=query(x,y)-query(x,b-1)-query(a-1,y)+query(a-1,b-1);
			printf("%d
",ans);
		}
	}
	return 0;
}

POJ 2299

先离散化,开一个权值树状数组

POJ 2352

在坐标上有(n)个星星,如果某个星星坐标为((x, y)), 它的左下位置为:((x0,y0),x0<=x)(y0<=y)。如果左下位置有(a)个星星,就表示这个星星属于(level x)

按照(y)递增,如果(y)相同则(x)递增的顺序给出(n)个星星,求出所有(level)水平的数量。

(x)为第一关键字升序排序,(y)为第二关键字升序排序

计数:排序后,将(y)的值一个一个加入树状数组中,加入前维护小于等于这个(y)的前缀和,得到的就是(level)

CF GYM 100741A Queries

开十个树状数组,分别表示模m=0,1,……的数的个数

每次加减的时候就找到单点修改

BZOJ 3289

树状数组维护区间逆序对

线段树

POJ 3488

裸题

LIS

(f_i=max{f_j+1})

线段树优化:1.离散化2.建立线段树,下标代表权值:对于所有$a_k=下标 (,最大的)f_k$是多少(线段树维护区间最大值)

BZOJ 1588

(1.Multiset)

(2.)建立一个权值线段树,对一个值(x),二分查找(对下标进行二分查找,寻找下标最大的(leq x)的权值不为(0)的点,下标最小的(geq x)的权值不为(0)的点,比较差值,取差值较小的)

BZOJ 3211

线段树维护区间和,每次修改暴力分治,如果出现一个区间都是(0)(1),打(tag),不再修改

Codeforces 718C

操作1:对([l,r])每个矩阵(*T^x)

操作2:对([l,r])中的矩阵求和,再(*[1,1])

(T)是斐波那契数列数列矩阵加速公式

Codeforces 85D

乍一看与线段树并无关系

1.离散化

2.建立权值线段树

3:

滚动合并:

BZOJ 3339

给出一个序列(A,Q)次询问,每次询问在([Li, Ri])(mex)是多少.
(N, Q ≤ 100000.)

将所有询问离线下来,按照(L)从小到大排序。提前处理好(1->i)的前缀(mex)

  for(int i=1;i<=n;i++){
        c[a[i]]=1;
        while(c[now]) now++;
        mex[i]=now;
    }

(next[i])表示下标为(i)的数下一次出现在哪个下标:

for(int i=n;i>=1;i--){
        next[i]=last[a[i]];
        if(next[i]==0) next[i]=n+1;
        last[a[i]]=i;
    }

(L)右移一,会将所有(L+1->next[i]-1)(>A[L])(mex)变为(A[L])

然后移动左端点,按照上面进行修改,对右端点进行查询(区间(mex)等于最右侧)

BZOJ 2124

给一个(1)(N)的排列({Ai}),询问是否存在(1<=p_1<p_2<p_3<p_4<p_5<…<p_{Len}<=N (Len>=3)),使得(A_{p1},A_{p2},A_{p3},…A_{pLen})是一个等差序列。

实质上问是否存在三元组((i,j,k)),满足(i<j<k)(A_k-A_j=A_j-A_i)

用二进制表示一个数是否已经选了,在某个位置的数若是(A[i]),若其向左数的(hash)不等于向右数的(hash),说明一定有(A[i]+x和A[i]-x)分别在(A[i])所在位置的两边,也就是有解.

↑上面这行目测看不懂,举个例子:

3
3 2 1

这是题目中的数据。我们用(01)串表示(1、2、3)这三个数选了没有,初始时是(“0 0 0”)

从左开始扫描,先选定(3),此时(01)串表示为("0 0 1")(3)的位置在串最右(原数集中(3)最大),左右不可能有数和它构成等差序列。

然后选定(2),此时(01)串表示为“(0 1 1)”,以(2)的位置为中心向左数,得到(0);向右数,在对称位置得到(1),这说明“比(2)(1)的数”和“比(2)(1)的数”一个选了(在左边)一个没选(在右边),那么它们就能构成等差序列了。

存储(hash)以判断(01)串是否相等,用线段树维护区间(hash)以方便查找,就大功告成了。

注意:由于是要判断一个位置左右“对称位置”是否不等,所以要存正序和逆序的(hash)。再举个例子:

(01)串:0 1 0 1 [1] 0 1 1 0 1,从中心位置向左数得到的串是“1 0 1 0”,向右数得到的是“0 1 1 0”(两边长度要保持等于最短的一边的长度)。

此处引用Bzoj2124 等差子序列

Luogu 2221

丢一篇题解

原文地址:https://www.cnblogs.com/zhuier-xquan/p/12267051.html