[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
丢一篇题解