树状数组
(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
丢一篇题解