1 条题解
-
0
考场上写了 3.5h 树形 dp 然后挂完了...
赛后补的是树的直径的做法。
有个很妙的结论,所有关键点到所有离它最远的关键点的所有路径都经过一个点:距离最远的一对关键点(即带权的树的直径)的中点(如果中点在点上我们设其为 ,如果中点在边上我们用 dfs 求出这条边的两个端点 , 有性质它们是被所有可能的直径经过次数最多的点)。
对于每个点 ,如果以它为根,离它最远的所有关键点的 为 ,只有删去 和 路径上的点才能对答案有贡献。
我们拉一条直径出来 , 是离 更近的直径中点, 是离 更近的直径中点,然后对于每一个关键点 ,如果它离 更远,那么删除 路径上的点会对答案有贡献,若它离 更远,那么删除 路径上的点会对答案有贡献,若同样远,显然此时直径的中点一定在点上,那么删除 路径上的点会对答案有贡献。
可以用树上差分维护每个点删除后的贡献。
#include<bits/stdc++.h> #define FL(i,a,b) for(int i=(a);i<=(b);i++) #define FR(i,a,b) for(int i=(a);i>=(b);i--) #define ll long long #define PII pair<int,int> using namespace std; const int MAXN = 1e5 + 10; int n,m; int d[MAXN]; bool vis[MAXN]; int L,R,D,C,Mxd=0; int fa[MAXN],siz[MAXN],son[MAXN]; int dis[MAXN],dep[MAXN],top[MAXN]; vector<PII>G[MAXN]; vector<int>E; void dfs1(int u,int f){ fa[u]=f; dep[u]=dep[f]+1; siz[u]=1,son[u]=0; for(auto i:G[u]){ int v=i.first,w=i.second; if(v==f) continue; dis[v]=dis[u]+w; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int tp){ top[u]=tp; if(son[u]) dfs2(son[u],tp); for(auto i:G[u]){ int v=i.first; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } int LCA(int u,int v){ while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } return (dep[u]<dep[v]?u:v); } int get_dis(int u,int v){ return dis[u]+dis[v]-(dis[LCA(u,v)]<<1); } void Add(int u,int v){ d[u]++; d[v]++; d[LCA(u,v)]--; d[fa[LCA(u,v)]]--; } void Calc(int u,int f){ for(auto i:G[u]){ int v=i.first; if(v==f) continue; Calc(v,u); d[u]+=d[v]; } } void Find(int u,int f){ if(d[u]==Mxd) ((!L)?L=u:R=u); for(auto i:G[u]){ int v=i.first; if(v==f) continue; Find(v,u); } } signed main(){ scanf("%d%d",&n,&m); FL(i,1,m){ int x; scanf("%d",&x); E.push_back(x); vis[x]=1; } FL(i,1,n-1){ int u,v,w; scanf("%d%d%d",&u,&v,&w); G[u].push_back({v,w}); G[v].push_back({u,w}); } dfs1(1,0); dfs2(1,1); int P,Q; int mx=0,id=-1; for(int i:E) if(get_dis(E[0],i)>mx) mx=get_dis(E[0],i),id=i; P=id; mx=0,id=-1; for(int i:E) if(get_dis(P,i)>mx) mx=get_dis(P,i),id=i; Q=id; for(int i:E){ if(get_dis(P,i)==mx) Add(P,i); if(get_dis(Q,i)==mx) Add(Q,i); } Calc(1,0); FL(i,1,n) Mxd=max(Mxd,d[i]); Find(P,0); D=get_dis(P,Q); FL(i,1,n) if((get_dis(i,P)<<1)==D&&(get_dis(i,Q)<<1)==D) C=i; memset(d,0,sizeof(d)); for(int i:E){ int dL=get_dis(i,P),dR=get_dis(i,Q); if(dL<dR) Add(i,R); else if(dL>dR) Add(i,L); else Add(i,C); } Calc(1,0); int ans=0,cnt=0; FL(i,1,n){ if(vis[i]) continue; if(d[i]>ans) ans=d[i],cnt=1; else if(d[i]==ans) cnt++; } printf("%d %d\n",ans,cnt); return 0; }
- 1
信息
- ID
- 6007
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- (无)
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者