[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\n",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

丢一篇题解

posted @ 2020-02-05 22:33  Sweetness  阅读(189)  评论(0编辑  收藏  举报