今天也是小小的把树的章节的内容大体过了一遍,总共有树上dp,LCA(最近公共祖先),树的直径,以及树上差分
P1395 会议
很经典的树上dp里面的换根dp问题,现在这里说几个数组
sz数组,用于统计以1为节点,每个节点的子树大小
sum数组,用于统计每个子树上总节点的累加
f 数组,用于统计以每个点为根的时候的路径长度
我们通过画图分析可知,换根公式为 f [ v ] = f [ u ] - sz [ u ] + ( n - sz [ u ])
我们就可以完成这个题目了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int u,v;
vector<int> e[50005];
int sz[50005];
int sum[50005];
int f[50005];
void dfs1(int v,int fa)
{
sz[v]=1;
for(int u:e[v])
{
if(u!=fa)
{
dfs1(u,v);
sz[v]+=sz[u];
}
}
}
void get(int v,int fa)
{
sum[v]=0;
for(int u:e[v])
{
if(u!=fa)
{
get(u,v);
sum[v]+=sz[u]+sum[u];
}
}
}
void dfs2(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
f[u]=f[v]-sz[u]+(n-sz[u]);
dfs2(u,v);
}
}
}
signed main()
{
cin>>n;
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,0);
get(1,0);
f[1]=sum[1];
dfs2(1,0);
int maxn=0x3f3f3f3f,flag=0;
for(int i=1;i<=n;i++)
{
if(f[i]<maxn)
{
flag=i;
maxn=f[i];
}
}
cout<<flag<<" "<<maxn;
return 0;
}
P3128 [USACO15DEC] Max Flow P
这道题乍一看,我没看出来哪里有最近公共祖先的思想,但是,后面在纸上写写画画的时候,发现 这其实就是最近公共祖先+树上差分
我们每次要在树上对每次提问的路径进行修改,那么我们这个是点修改,因此我们需要将经过的两个点的差分数组dif++,最近公共祖先和其父节点dif--
然后就直接出来了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int u,v;
vector<int> e[50005];
int dep[50005];
int f[50005][17];
int dif[50005];
int ans;
void dfs(int v,int fa)
{
dep[v]=dep[fa]+1;
f[v][0]=fa;
for(int i=1;i<17;i++)
{
f[v][i]=f[f[v][i-1]][i-1];
}
for(int u:e[v])
{
if(u!=fa)
{
dfs(u,v);
}
}
}
int lca(int u,int v)
{
if(dep[u]<dep[v])
{
swap(u,v);
}
for(int i=17-1;i>=0;i--)
{
if(dep[f[u][i]]>=dep[v])
u=f[u][i];
}
if(u==v)
return v;
for(int i=16;i>=0;i--)
{
if(f[u][i]!=f[v][i])
{
u=f[u][i];
v=f[v][i];
}
}
return f[u][0];
}
void sign_up(int v,int fa)
{
for(int u:e[v])
{
if(u!=fa)
{
sign_up(u,v);
dif[v]+=dif[u];
}
}
ans=max(ans,dif[v]);
}
signed main()
{
cin>>n>>k;
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
for(int i=1;i<=k;i++)
{
cin>>u>>v;
dif[u]++;
dif[v]++;
dif[lca(u,v)]--;
dif[f[lca(u,v)][0]]--;
}
sign_up(1,0);
cout<<ans;
return 0;
}
P3398 仓鼠找 sugar
归根到底,其实还是最近公共祖先的思想,但是我们这道题要想清楚的是,我们如何判断有两条路径相交,一方面是如果两条路径相交,那么第一条路径的公共祖先一定会在第二条路径上,但是我们如何去判断一个点在一个路径上,因此我们需要用到深度数组,如果其公共祖先在a,b这条路径上,那么a~公共祖先的距离+b~公共祖先的距离=a~b的距离
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int u,v;
vector<int> e[100005];
int dep[100005];
int f[100005][18];
void dfs(int v,int fa)
{
dep[v]=dep[fa]+1;
f[v][0]=fa;
for(int i=1;i<18;i++)
{
f[v][i]=f[f[v][i-1]][i-1];
}
for(int u:e[v])
{
if(u!=fa)
{
dfs(u,v);
}
}
}
int lca(int u,int v)
{
if(dep[u]<dep[v])
swap(u,v);
for(int i=17;i>=0;i--)
{
if(dep[f[u][i]]>=dep[v])
u=f[u][i];
}
if(u==v)
return v;
for(int i=17;i>=0;i--)
{
if(f[u][i]!=f[v][i])
{
u=f[u][i];
v=f[v][i];
}
}
return f[u][0];
}
int dis(int u,int v)
{
int LCA=lca(u,v);
return abs(dep[u]-dep[LCA])+abs(dep[LCA]-dep[v]);
}
signed main()
{
cin>>n>>q;
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
int a,b,c,d;
for(int i=1;i<=q;i++)
{
cin>>a>>b>>c>>d;
int x=lca(a,b),y=lca(c,d);
if((dis(a,y)+dis(b,y)==dis(a,b))||(dis(c,x)+dis(d,x)==dis(c,d)))
cout<<"Y\n";
else
cout<<"N\n";
}
return 0;
}