两点间的路径每个点给一个单位的压力,询问最大压力的点。
LCA用的离线,点在树上差分
#include<bits/stdc++.h>
#define mxn 500005
using namespace std;
int n,k,x,y,hd[mxn],fa[mxn],cnt,vis[mxn],dfn[mxn],cf[mxn],du[mxn],s,nod[mxn],bis[mxn],up[mxn];
int hk[100005];
struct Edge{
int nxt,to,lca;
}edge[(mxn+100005)<<1];
void add(int u,int v)
{
cnt++;
edge[cnt].to =v;
edge[cnt].nxt=hd[u];
hd[u]=cnt;
}
void add_ask(int u,int v)
{
cnt++;//边不能还是从0开始
edge[cnt].to=v;
edge[cnt].nxt=hk[u];
hk[u]=cnt;
}
void init()
{
int num=0;
for(int i=1;i<=n;i++)
{
fa[i]=i;
if(du[i]>num){
num=du[i];
s=i;
}
}
memset(vis,0,sizeof vis);
}
int find(int u)
{
if(fa[u]==u) return u;
return fa[u]=find(fa[u]);
}
//离线
//里面的并查集不压缩
int tim=0;
void tarjan(int u,int f)
{
for(int i=hd[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(!vis[v] && v!=f)
{
up[v]=u;
tarjan(v,u);
fa[v]=u;
}
}//u访问完了
vis[u]=1;
//先不更新父亲,先询问
for(int j=hk[u];j;j=edge[j].nxt)
{
int w=edge[j].to;
if(vis[w])
{
//u,v的lca是find(w)
edge[j].lca=find(w);
// cout<<u<<","<<w<<","<<edge[j].lca<<endl;
}
}
dfn[u]=tim++;
}
int dfs(int u,int f)//类似于记忆化
{
int sum=0;
if(du[u]==1) return cf[u];
if(nod[u]) return nod[u];
if(cf[u]) sum+=cf[u];
for(int i=hd[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(v!=f)
{
sum+=dfs(v,u);
}
}
nod[u]=sum;//nod[4]
return sum;
}
int p[mxn],q[mxn];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
du[x]++,du[y]++;
add(x,y);add(y,x);
}
// for(int u=1;u<=n;u++)
// {
// cout<<u<<","<<du[u]<<endl;
// for(int j=hd[u];j;j=edge[j].nxt)
// {
// cout<<u<<","<<edge[j].to<<endl;
// }
// }
for(int i=1;i<=k;i++)
{
scanf("%d%d",&p[i],&q[i]);
add_ask(p[i],q[i]);add_ask(q[i],p[i]); //如何表示有询问? 还是邻接表,虚拟出一条边
}
// for(int u=1;u<=n;u++)
// {
// for(int j=hk[u];j;j=edge[j].nxt)
// {
// cout<<u<<","<<edge[j].to<<endl;
// }
// }
init();
tarjan(s,0);
for(int i=1;i<=k;i++)//怎么取出两个点的lca ?
{
cf[p[i]]++;cf[q[i]]++;
int o;
if(dfn[p[i]]>dfn[q[i]])
o=p[i];
else o=q[i];
if(bis[o]) continue;//比如4-5,4是结束时间戳久的那个,然后遍历所有以4为端点的lca
bis[o]=1;//然后5-4,4是结束时间戳,然后又遍历了一遍,重复记录lca
for(int j=hk[o];j;j=edge[j].nxt)
{
cf[edge[j].lca]--,cf[up[edge[j].lca]]--;//(1).这个父亲已经改变了,因为并查集中的路径压缩
}
}
// for(int i=1;i<=n;i++)
// cout<<i<<","<<cf[i]<<endl;
int mx=0;
//不知道最后一块怎么写,不是暴力一个一个写么?从叶节点向根节点统计
// for(int x=1;x<=n;x++)
// {
// if(du[x]==1)//叶子节点
// {
// while(x!=s)
// {
// cf[fa[x]]+=cf[x];//因为路径压缩,这里的fa已经不是原来的父节点了
// if(cf[fa[x]]>mx) mx=cf[fa[x]];
// }
// }
// }
dfs(s,0);
for(int i=1;i<=n;i++)
if(mx<nod[i]) mx=nod[i];
cout<<mx<<endl;
}