树状数组
单点修改,区间询问
#include<iostream> #include<cstdio> using namespace std; int read() //读入优化 { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<3)+(a<<1)+(ch-'0'); ch=getchar(); } return a*x; } int n,m,k,x,y; //n个数,m次操作 //k=1,第x个数加上y //k=0,问区间[x,y]的和
int a[500001],c[500001];
int lowbit(int x) //求lowbit { return x&(-x); }
void update(int x,int y) { for(;x<=n;x+=lowbit(x)) c[x]+=y; //第x个数加上y }
int sum(int x) //求区间[1,x]的和 { int ans=0; for(;x;x-=lowbit(x)) ans+=c[x]; return ans; }
int main() { n=read();m=read(); for(int i=1;i<=n;i++) { a[i]=read(); update(i,a[i]); //先建好树 } for(int i=1;i<=m;i++) { k=read();x=read();y=read(); if(k==1) update(x,y); else printf("%d\n",sum(y)-sum(x-1)); //前缀和做差 } return 0; }
区间修改,单点询问
#include<iostream> #include<cstdio> using namespace std; int read() //读入优化 { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<3)+(a<<1)+(ch-'0'); ch=getchar(); } return a*x; } int n,m,k,x,y,v; //n个数,m次操作 //k=1,区间[x,y]加上v //k=2,询问第x个数的值 int a[500001],c[500001],d[500001]; //a存原数列,c是树状数组,d是差分数组 int lowbit(int x) //求lowbit { return x&(-x); } void update(int x,int y) { for(;x<=n;x+=lowbit(x)) c[x]+=y; //第x个数加上y } int sum(int x) //求区间[1,x]的和 { int ans=0; for(;x;x-=lowbit(x)) ans+=c[x]; return ans; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { a[i]=read(); d[i]=a[i]-a[i-1]; //求差分数组 update(i,d[i]); //建树 } for(int i=1;i<=m;i++) { k=read(); if(k==1) { x=read();y=read();v=read(); //区间[x,y]加上v update(x,v); //差分数组的变化 update(y+1,-v); } else { x=read(); printf("%d\n",sum(x)); } } return 0; }
线段树
单点修改,区间询问
#include<iostream> #include<cstdio> using namespace std; int read() //读入优化 { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<3)+(a<<1)+(ch-'0'); ch=getchar(); } return a*x; } int n,m,k,x,y; //n个数,m次操作 //k=1,第x个数加上y //k=2,询问区间[x,y]的和 int a[500001],sum[500001]; //a是原数列,sum是维护的区间和 void build(int node,int l,int r) //建树 { if(l==r) //叶子结点 { sum[node]=a[l]; //直接赋值 return ; } int mid=(l+r)>>1; build(node*2,l,mid); //分别建好左右子树 build(node*2+1,mid+1,r); sum[node]=sum[node*2]+sum[node*2+1]; //加起来就是根结点的区间和 } void add(int node,int l,int r,int x,int k) //给第x个数加上k { if(l==r&&l==x) //找到了叶子结点且正好是区间[x,x] { sum[node]+=k; return ; } int mid=(l+r)>>1; if(x<=mid) add(node*2,l,mid,x,k); //看是否在左子树里 else add(node*2+1,mid+1,r,x,k); //否则就在右子树里,注意这里能用else是因为这是单点修改 sum[node]=sum[node*2]+sum[node*2+1]; } int ask(int node,int l,int r,int x,int y) //询问区间和 { if(x<=l&&r<=y) return sum[node]; //[l,r]被完全包含在[x,y]内的话直接返回 int mid=(l+r)>>1; int rnt=0; if(x<=mid) rnt+=ask(node*2,l,mid,x,y); //找左右子树是否有交集 if(y>mid) rnt+=ask(node*2+1,mid+1,r,x,y); return rnt; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); build(1,1,n); //建树 for(int i=1;i<=m;i++) { k=read();x=read();y=read(); if(k==1) add(1,1,n,x,y); else printf("%d\n",ask(1,1,n,x,y)); } return 0; }
区间修改,区间询问
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cstring> #include<string> #include<cmath> #include<ctime> #include<set> #include<vector> #include<map> #include<queue> #define N 1000005 #define M 1000005 #define ls (t*2) #define rs (t*2+1) #define mid ((l+r)/2) using namespace std; long long i,j,m,n,p,k,lazy[N*4],a[N],x,c,l,r; //lazy数组存每个结点的懒标记,sum数组存每个结点的区间和,注意四倍空间 long long ans,sum[N*4]; void build(int l,int r,int t) //初始化sum的值,为原先的区间和 { if (l==r) sum[t]=a[l]; //只有一个结点 else { build(l,mid,ls); //找它的左儿子 build(mid+1,r,rs); //找它的右儿子 sum[t]=sum[ls]+sum[rs]; //该结点的区间和等于它的左儿子加上它的右儿子的区间和 } } void down(int t,int len) //对lazy标记进行下传 { if (!lazy[t]) return; //如果没有lazy标记,那么直接返回 sum[ls]+=lazy[t]*(len-len/2); //求左儿子的新区间和,len-len/2是左儿子的长度 sum[rs]+=lazy[t]*(len/2); //求右儿子的新区间和,len/2是右儿子的长度 lazy[ls]+=lazy[t]; //传给左右儿子,累积lazy标记 lazy[rs]+=lazy[t]; lazy[t]=0; //父亲结点的lazy标记已经传下去了,所以要清空 } void modify(int ll,int rr,long long c,int l,int r,int t) //[ll,rr]整体加上c,当前节点代表的区间位[l,r] { if (ll<=l&&r<=rr) //如果当前分解的区间在所给的区间内 { sum[t]+=(r-l+1)*c; //对[l,r]区间的影响就是加上了(r-l+1)*c lazy[t]+=c; //加上lazy标记 } else //如果当前分解的区间未完全被包含在内的话 { down(t,r-l+1); //要往下细分就要下传lazy标记 if (ll<=mid) modify(ll,rr,c,l,mid,ls); //如果与左区间有交集,那么我们就去细分左区间 if (rr>mid) modify(ll,rr,c,mid+1,r,rs); //如果与右区间有交集,那么我们就去细分右区间 sum[t]=sum[ls]+sum[rs];//更新一下区间和 } } void ask(long long ll,long long rr,long long l,long long r,long long t) //[ll,rr]是要查询的区间,[l,r]是当前分解的区间 { if (ll<=l&&r<=rr) ans+=sum[t]; //代表着找到了完全被包含在内的一个区间,所以直接返回这个区间的区间和 else //如果有未完全被包含在内的 { down(t,r-l+1); //将lazy标记下传并继续细分它的儿子 if (ll<=mid) ask(ll,rr,l,mid,ls); //如果与左区间有交集就细分左区间 if (rr>mid) ask(ll,rr,mid+1,r,rs);//如果与右区间有交集就细分右区间 } } int main() { scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%d",&a[i]); build(1,n,1); for(long long i=1;i<=m;i++) { long long p; scanf("%lld",&p); if(p==1) { long long l,r,k; scanf("%lld%lld%lld",&l,&r,&k); modify(l,r,k,1,n,1); } if(p==2) { long long l,r; scanf("%lld%lld",&l,&r); ans=0; ask(l,r,1,n,1); printf("%lld\n",ans); } } return 0; }
ST表
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int a[100001],f[100001][20]; int read() { char ch=getchar(); long long a=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') { a=a*10+(ch-'0'); ch=getchar(); } return a; } int main() { int n,m; n=read(),m=read(); for(int i=1;i<=n;i++) { a[i]=read(); f[i][0]=a[i]; //初始化 } for(int j=1;(1<<j)<=n;j++) //注意j在外层 for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); //状态转移方程 for(int i=1;i<=m;i++) { int l=read(); int r=read(); int k=(int)(log((double)(r-l+1))/log(2.0)); int ans=max(f[l][k],f[r-(1<<k)+1][k]); printf("%d\n",ans); } return 0; }
最近公共祖先LCA
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=500001; int head[2*maxn],to[2*maxn],next[2*maxn],grand[2*maxn][21],dep[maxn]; int n,m,s,edge_sum=0; void add(int x,int y) //链表存图 { next[++edge_sum]=head[x]; head[x]=edge_sum; to[edge_sum]=y; } void dfs(int v,int deep) //dfs求出每个点的深度 { dep[v]=deep; for(int i=head[v];i>0;i=next[i]) { int u=to[i]; if(!dep[u]) dfs(u,deep+1),grand[u][0]=v; } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); //让x的深度大于y for(int i=20;i>=0;i--) //跳到同一深度 if(dep[y]<=dep[x]-(1<<i)) x=grand[x][i]; if(x==y) return y; for(int i=20;i>=0;i--) { if(grand[x][i]!=grand[y][i]) //跳不到同一点就往上跳 { x=grand[x][i]; y=grand[y][i]; } } return grand[x][0]; //最后再跳一下肯定是LCA } int read() { char ch=getchar(); int a=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') { a=a*10+(ch-'0'); ch=getchar(); } return a; } int main() { memset(head,0,sizeof(head)); n=read(),m=read(),s=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); add(x,y); add(y,x); } grand[s][0]=s; dfs(s,1); for(int i=1;(1<<i)<=n;i++) for(int j=1;j<=n;j++) grand[j][i]=grand[grand[j][i-1]][i-1]; //状态转移方程 for(int i=1;i<=m;i++) { int x=read(),y=read(); printf("%d\n",lca(x,y)); } return 0; }
快读模板
int read() { char ch=getchar(); int a=0,x=1; while(ch<'0'||ch>'9') { if(ch=='-') x=-x; ch=getchar(); } while(ch>='0'&&ch<='9') { a=(a<<3)+(a<<1)+(ch-'0'); ch=getchar(); } return a*x; }