【图论】树论

经典例子如下:

lca

树上差分

树的重心

树的直径

树的中心

树链剖分

目录

最近公共祖先

树的直径

树的中心

例题


1.1最近公共祖先

P3379 【模板】最近公共祖先(LCA) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

简单模板,过了无数遍

P3128 [USACO15DEC] Max Flow P - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

树上差分

就差一点

#include<iostream>
using namespace std;
int  n,k;
int dep[50001];
int fa[50001][21];
int a[50001];
int b[50001];

struct EDGE{

int next;
int to;


}edge[100001];
int head[50001];
int tot=0;
void addedge(int u,int v){
edge[++tot].next=head[u];
edge[tot].to=v;
head[u]=tot;
}
void dfs(int u,int father){

dep[u]=dep[father]+1;
for(int i=1;(1<<i)<=dep[u];i++){
    fa[u][i]=fa[fa[u][i-1]][i-1];
}

for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;
    if(v==father)continue;
    fa[v][0]=u;
    dfs(v,u);
}

}

void dfss(int u,int father){


cout<<"现在u,fa"<<u<<" "
//a[u]=子树a+差分b
    a[u]=b[u];


for(int i=head[u];~i;i=edge[i].next){

    int v=edge[i].to;cout<<"现在v"<<v<<endl;
    if(v==father)continue;

    dfs(v,u);
    a[u]+=a[v];
    for(int i=1;i<=n;i++){

        cout<<a[i]<<" ";}
        cout<<endl;
}


}

int  lca(int x,int y){

if(dep[x]<dep[y])swap(x,y);

for(int i=20;i>=0;i--){
    if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
}


for(int i=20;i>=0;i--){
    if(fa[x][i]!=fa[y][i]){
        x=fa[x][i];
        y=fa[y][i];
    }
}


return fa[x][0];

}

int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)head[i]=-1;

for(int i=1;i<=n-1;i++){
    int u,v;
    cin>>u>>v;
    addedge(u,v);
    addedge(v,u);
}

dfs(1,0);




while(k--){
int x,y;
cin>>x>>y;
int mf=lca(x,y);


b[x]++;
b[y]++;
b[mf]--;
if(mf != 1) b[fa[mf][0]]--;

}







cout<<"现在检验5的边";
for(int i=head[5];~i;i=edge[i].next)
{
cout<<edge[i].to<<" ";

}
cout<<"检验结束"<<endl;

dfss(1,0);
int ans=0;



for(int i=1;i<=n;i++){



ans=max(ans,a[i]);
}

cout<<ans;

}

P5002 专心OI - 找祖先 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

n^3 tle

1.2树的几种dfs

树的重心、直径、中心无非是几种dfs的组合拳

1.2.1求sz[u]/son[u]


void dfssz(int u,int father){
sz[u]=1;
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;
    if(v==father)continue;
    dfssz(v,u);
    sz[u]+=sz[v];
    if(sz[son[u]]<sz[v])son[u]=v;
}


}

1.3树的重心

1.3.1重心性质

1、树上所有的点到树的重心的距离之和是最短的,如果有多个重心,那么总距离相等。

2、插入或删除一个点,树的重心的位置最多移动一个单位。

3、若添加一条边连接2棵树,那么新树的重心一定在原来两棵树的重心的路径上。

1.删除重心后的所有子树,节点树都不超过原树的一半。
2.一颗树最多两个重心,而且相邻。
3.树中所有的节点到重心的距离之和最小,要是有两个重心,那么他们的距离相等。
4.这颗树要是删除或者增加一条边,重心最多只移动一条边。

#include<iostream>
using namespace std;
#include<vector>
#define INF 0x3f3f3f3f
struct EDGE{

int next;
int to;


}edge[400001];
int head[20001];
int son[20001];
int sz[20001];
int tot=0;
void addedge(int u,int v){
edge[++tot].next=head[u];
edge[tot].to=v;
head[u]=tot;
}

void dfssz(int u,int father){
sz[u]=1;
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;
    if(v==father)continue;
    dfssz(v,u);
    sz[u]+=sz[v];
    if(sz[son[u]]<sz[v])son[u]=v;
}


}


int main(){
int t;
cin>>t;

while(t--){
        int n;
cin>>n;
for(int i=1;i<=n;i++)head[i]=-1;
for(int i=1;i<=n-1;i++){
    int u,v;cin>>u>>v;
    addedge(u,v);
    addedge(v,u);
}

dfssz(1,0);

int ans;int temp=INF;
for(int i=1;i<=n;i++){
    if(temp>max(sz[son[i]],n-sz[i])){
        temp=max(sz[son[i]],n-sz[i]);
        ans=i;
    }
}
cout<<ans<<" "<<temp<<endl;
}


}

1.3树的直径

树的直径两种求法_求树的直径-CSDN博客

法一:dfs找距离任意一点最远的点u

dfs找距离u最远的一点v

uv即为直径

只能用于没有付权边

void dfs(int u,int father){

for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(v==father)continue;

d[v]=d[u]+edge[i].w;
dfs(v,u);
}

}

int main(){
dfs(1,0);
//选出d[s]最大
dfs(s,0);
//选出d[v]最大
cout<<d[v];

}

法二:

某一点的最短路和次短路

某一点到其子节点的最长距离和次长距离,加起来就是直径

void dfsdp(int u,int father)
{

for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;
    int w=edge[i].w;
    if(v==father)continue;
    dfsdp(v,u);


    if(d1[u]<d1[v]+w){
        d2[u]=d1[u];
        d1[u]=d1[v]+w;
    }
    else if(d2[u]<d1[v]+w){
d2[u]=d1[v]+w;
    }
}

}

Cow Marathon - OpenJ_Bailian 1985 - Virtual Judge (vjudge.net.cn)

#include<iostream>
using namespace std;
int n,m;

struct EDGE{
int next;
int to;
int w;

}edge[80001];
int head[40001];
int d[40001];
int tot=0;
void addedge(int u,int v,int w){
edge[++tot].next=head[u];
edge[tot].to=v;
edge[tot].w=w;
head[u]=tot;
}

void dfs(int u,int father){

for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;
    if(v==father)continue;
    d[v]=d[u]+edge[i].w;
    dfs(v,u);
}


}

int main(){

cin>>n>>m;
for(int i=1;i<=n;i++)head[i]=-1;
for(int i=1;i<=m;i++){
    int u,v,w;char s;
    cin>>u>>v>>w>>s;
    addedge(u,v,w);
    addedge(v,u,w);
}

dfs(1,0);
int xuan1=-1;int xuan2=0;
for(int i=1;i<=n;i++){
    if(d[i]>xuan2){
        xuan1=i;
        xuan2=d[i];
    }
    d[i]=0;

}

dfs(xuan1,0);
xuan1=-1;xuan2=0;

for(int i=1;i<=n;i++){
    if(d[i]>xuan2){
        xuan1=i;
        xuan2=d[i];
    }

}
cout<<xuan2;


}

树的中心

树的重心是节点数

树的重心是路径长度

到别的点的最长距离最小的点

-》枚举每个点

、子树的最长距离直径中有板子   +、向上走的


//走下面的最长
void dfsd12(int u,int father){
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;int w=edge[i].w;
    if(v==father)continue;

    dfsd12(v,u);

    if(d1[u]<d1[v]+w){
        d2[u]=d1[u];
        d1[u]=d1[v]+w;
        p1[u]=v;//path
    }
    else if(d2[u]<d1[v]+w){
        d2[u]=d1[v]+w;
    }
}
}

//走上面的最长
void dfsup(int u,int father){
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;int w=edge[i].w;
    if(v==father)continue;

    if(p1[u]==v){
        up[v]=max(d2[u],up[u])+w;
    }else {
        up[v]=max(d1[u],up[u])+w;
    }

    dfsup(v,u);
}

}

1073. 树的中心 - AcWing题库


//走下面的最长
void dfsd12(int u,int father){
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;int w=edge[i].w;
    if(v==father)continue;

    dfsd12(v,u);

    if(d1[u]<d1[v]+w){
        d2[u]=d1[u];
        d1[u]=d1[v]+w;
        p1[u]=v;//path
    }
    else if(d2[u]<d1[v]+w){
        d2[u]=d1[v]+w;
    }
}
}

//走上面的最长
void dfsup(int u,int father){
for(int i=head[u];~i;i=edge[i].next){
    int v=edge[i].to;int w=edge[i].w;
    if(v==father)continue;

    if(p1[u]==v){
        up[v]=max(d2[u],up[u])+w;
    }else {
        up[v]=max(d1[u],up[u])+w;
    }

    dfsup(v,u);
}

}

例题

树的直径 - OI Wiki (oi-wiki.org)

把这的刷完

Tree Destruction - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

做出来,有顺序?

U370080 [ZOJ - 3820] Building Fire Stations - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

不会 

Balancing Act - POJ 1655 - Virtual Judge (vjudge.net.cn)

简单

P1364 医院设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

floyd可过

状态转移?不能理解

Problem - C - Codeforces

用结论树最多两个重心,相邻

Problem - 708C - Codeforces

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值