题意:
有一颗n个节点的树,有k次操作,每次操作给出二个节点,然后将二个节点及二个节点之间的节点权值都加一,然后求最后树中权值最大的节点的权值为多少?
思路:
lca+差分:
用lca求出二个节点u、v的最近公共祖先k,k的父亲节点为kp;
要使u、v路经上节点权值加一则val[u]、val[v]进行加一,表示经过u、v节点时值加一,然后k位置此时相当与加了二,所以val[k]要减一,最后val[kp]减一表示经过kp节点时值减一。
求点的权值时从叶子节点开始向根节点计算,取最大值当结果即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define eps 1e-8
using namespace std;
const ll inf=1e9+7;
int val[100005], dep[100005], parent[21][100005], qi=0, ma=0;
vector<int> g[100005];
void dfs(int v,int f,int k)
{
dep[v]=k;
parent[0][v]=f;
for(int i=0; i<g[v].size(); i++)
{
int u=g[v][i];
if(u==f)
{
continue;
}
dfs(u,v,k+1);
}
}
int dfs1(int v,int f)
{
int z=val[v];
for(int i=0; i<g[v].size(); i++)
{
int u=g[v][i];
if(u==f)
{
continue;
}
z=z+dfs1(u,v);
}
ma=max(ma,z);
return z;
}
int quitv(int u,int v)
{
if(dep[u]<dep[v])
{
swap(u,v);
}
int k=dep[u]-dep[v];
for(int i=18; i>=0; i--)
{
if((k>>i)&1)
{
u=parent[i][u];
}
}
if(u==v)
{
return u;
}
for(int i=18; i>=0; i--)
{
if(parent[i][u]!=parent[i][v])
{
u=parent[i][u];
v=parent[i][v];
}
}
return parent[0][u];
}
int main()
{
int n, q;
scanf("%d%d",&n,&q);
for(int i=1; i<n; i++)
{
int u, v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,-1, 1);
for(int k=1; k<=18; k++)
{
for(int i=1; i<=n; i++)
{
if(parent[k-1][i]==-1)
{
parent[k][i]=-1;
}
else
{
parent[k][i]=parent[k-1][parent[k-1][i]];
}
}
}
while(q--)
{
int u, v;
scanf("%d%d",&u,&v);
int k=quitv(u,v);
val[k]--;
if(parent[0][k]!=-1)
val[parent[0][k]]--;
val[u]++;
val[v]++;
}
dfs1(1,-1);
cout << ma << endl;
return 0;
}

京公网安备 11010502036488号