题目大意
给你一棵树 给你k对点 这两点之间的最短路径上的每个点权值加一 问最后哪个点的权值最大
输入
第一行输入n,k 表示点的个数和修改的个数
接下来的n-1行 每行两个数u,v 表示u,v间有一条边
接下来k行 每行两个数u,v 表示要修改的路径
输出
点权最大的点的权值。
这道题可以用树剖+线段树来做...
但是代码量太大了 果断放弃
有一种很方便的方法 叫做树上差分
就是每对点 val[u]+1 val[v]+1 设p为u,v 的最近公共祖先
则val[p]-1 val[p的父亲]-1
某个点的点权就是它的子树和
所以模板就是 树剖的两遍dfs 还有求子树和的一遍dfs
最后输出最大的子树和就可以了
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=50000+10;
const int M=100000+10;
const int oo=1e9;
int n,k,head[N],nex[M],tov[M],son[N],dis[N],ans;
int in[N],out[N],seq[N],pre[N],val[N],f[N];
int tot,size[N],idc,top[N];
void add(int u,int v){
tot++;
nex[tot]=head[u];
tov[tot]=v;
head[u]=tot;
}
int max(int a,int b){
return a<b?b:a;
}
void dfs1(int x,int fa){
size[x]=1;
pre[x]=fa;
for(int i=head[x];i;i=nex[i]){
int v=tov[i];
if(v==fa) continue;
dis[v]=dis[x]+1;
dfs1(v,x);
size[x]+=size[v];
if(!son[x]||size[v]>size[son[x]]) son[x]=v;
}
}
void dfs2(int x,int tp){
seq[x]=++idc;
top[x]=tp;
in[x]=idc;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nex[i]){
int v=tov[i];
if(v==pre[x]||v==son[x]) continue;
dfs2(v,v);
}
out[x]=idc;
}
void dfs3(int u){
f[u]=val[u];
for(int i=head[u];i;i=nex[i]){
int v=tov[i];
if(v==pre[u]) continue;
dfs3(v);
f[u]+=f[v];
}
ans=max(ans,f[u]);
}
int find_lca(int u,int v){
while(top[u]!=top[v]){
if(dis[top[u]]<dis[top[v]]) swap(u,v);
u=pre[top[u]];
}
return dis[u]<dis[v]?u:v;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
int root=1;
dfs1(root,root);
dfs2(root,root);
for(int i=1;i<=k;i++){
int u,v;
scanf("%d%d",&u,&v);
val[u]++;
val[v]++;
int p=find_lca(u,v);
val[p]--;
val[pre[p]]--;
}
ans=oo;
ans*=-1;
dfs3(root);
printf("%d",ans);
return 0;
}