链式前向星
有向图
#include<bits/stdc++.h>
using namespace std;
int head[1010];
struct edge{
int to;
int w;
int next;
};
int v,e;
int main(){
cin>>v>>e;
edge point[v+10];
for(int i=1;i<=e;i++){/*~.~*/
int x,y;
int w_;
scanf("%d%d%d",&x,&y,&w_);
point[i].to=y;
point[i].w=w_;
point[i].next=head[x];
head[x]=i;
/*>.<*/
}
return 0;
}
\space
特殊情况
无向图
可在/>.</处添加反向的一条边
将/~ . ~/处
e
e
e改为
e
∗
2
e*2
e∗2
\space
树
e
=
n
−
1
e=n-1
e=n−1,/~ . ~/处也要改为
e
∗
2
e*2
e∗2
\space
别于邻接矩阵
邻接表与链式前向星相似,故不予讨论
邻接矩阵 | 链式前向星 | |
---|---|---|
存储空间 | O ( n 2 ) O(n^2) O(n2) | O ( m ) O(m) O(m) |
修改时间 | O ( 1 ) O(1) O(1) | O ( n ) O(n) O(n) |
查找时间 | O ( n 2 ) O(n^2) O(n2) | O ( m ) O(m) O(m) |
\space |
线段树
模板题
点此蹦过去
注
意
数
据
范
围
!
!
!
{\color{red}{注意数据范围!!!}}
注意数据范围!!!
#include<bits/stdc++.h>
using namespace std;
long long d[4000010];
int a[4000010];
long long b[4000010];
int n;
void build(int l,int r,int p){
if(l==r){
d[p]=a[l];
return;
}
else{
int m=l+((r-l)>>1);
build(l,m,p*2);
build(m+1,r,p*2+1);
d[p]=d[p*2]+d[(p*2)+1];
}
}
long long getsum(int in_l,int in_r,int fi_l,int fi_r,int p){
if(in_l>=fi_l&&in_r<=fi_r){
return d[p];
}
int m=in_l+((in_r-in_l)>>1);
if(b[p]){
d[p*2]+=b[p]*(m-in_l+1);
d[p*2+1]+=b[p]*(in_r-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
long long sum=0;
if(m>=fi_l) sum+=getsum(in_l,m,fi_l,fi_r,p*2);
if(m+1<=fi_r) sum+=getsum(m+1,in_r,fi_l,fi_r,p*2+1);
return sum;
}
void update(int up_l,int up_r,int c,int in_l,int in_r,int p){
if(in_l>=up_l&&in_r<=up_r){
d[p]+=(in_r-in_l+1)*c;
b[p]+=c;
return;
}
int m=in_l+((in_r-in_l)>>1);
if(b[p]&&in_l!=in_r){
d[p*2]+=b[p]*(m-in_l+1);
d[p*2+1]+=b[p]*(in_r-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
if(up_l<=m) update(up_l,up_r,c,in_l,m,p*2);
if(m+1<=up_r) update(up_l,up_r,c,m+1,in_r,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
int main(){
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
for(int i=1;i<=m;i++){
int way;
scanf("%d",&way);
if(way==1){
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
update(x,y,k,1,n,1);
}
else{
int x,y;
scanf("%d%d",&x,&y);
long long ans=getsum(1,n,x,y,1);
printf("%lld\n",ans);
}
}
return 0;
}
原理
将“线段”分为类二叉堆,通过相同方式存储、查找,修改时将大的范围分治,分为较小范围计算
线段树 | 前缀和 | |
---|---|---|
修改 | O ( l o g N ) O(logN) O(logN) | O ( N ) O(N) O(N) |
查找 | O ( l o g N ) O(logN) O(logN) | O ( 1 ) O(1) O(1) |
最近公共祖先( L C A LCA LCA)
#include<bits/stdc++.h>
using namespace std;
int head[500020];
struct edge{
int to;
int next;
};
int a[500020];
edge point_[1000020];//1
int deep[500020];
int fa[500020][25];
int n,m,s;
void action_(int x,int from){//2
for(int i=head[x];i!=0;i=point_[i].next){
if(point_[i].to!=from){
deep[point_[i].to]=deep[x]+1;
fa[point_[i].to][0]=x;
action_(point_[i].to,x);
}
}
return;
}
int f(int i,int j){
if(fa[i][j]!=0) return fa[i][j];
else if(deep[i]-(1<<j)<=0) return fa[i][j]=-1;
else return fa[i][j]=f(f(i,j-1),j-1);//3
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=2*(n-1);i++){
int x,y;
scanf("%d%d",&x,&y);
point_[i].to=y;
point_[i].next=head[x];
head[x]=i;
i++;
point_[i].to=x;
point_[i].next=head[y];
head[y]=i;
}
deep[s]=1;
action_(s,s);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(deep[x]<=deep[y]) swap(x,y);
for(int i=19;i>=0;i--){//4
if(f(x,i)!=-1){
if(deep[f(x,i)]>=deep[y]){
x=f(x,i);
}
}
}
while(deep[x]>deep[y]) x=f(x,0);
for(int i=19;i>=0;i--){
if(f(x,i)!=f(y,i)){
x=f(x,i);
y=f(y,i);
}
if(f(x,0)==f(y,0)) break;
}
if(x!=y) printf("%d\n",f(x,0));//5
else printf("%d\n",x);
}
return 0;
}
需注意的点
- 因为是无向图,所以每条边存两次,故数组开两倍,否则 R E RE RE
- f r o m from from的作用:保证一定是由根节点往下,深度递增
- 递归可以用记忆化储存
- 易证,向上蹦的距离一定单调减,故一次循环足矣
- 例:
如果查找3和5,可能在深度一样时就是答案,故需要特判