1 条题解

  • 0
    @ 2026-8-11 14:41:25

    考场上写了 3.5h 树形 dp 然后挂完了...

    赛后补的是树的直径的做法。

    有个很妙的结论,所有关键点到所有离它最远的关键点的所有路径都经过一个点:距离最远的一对关键点(即带权树的直径)的中点(如果中点在点上我们设其为 CC,如果中点在边上我们用 dfs 求出这条边的两个端点 L,RL,RL,RL,R 有性质它们是被所有可能的直径经过次数最多的点)。

    对于每个点 uu,如果以它为根,离它最远的所有关键点的 lca\mathrm{lca}vv,只有删去 uuvv 路径上的点才能对答案有贡献。

    我们拉一条直径出来 (P,Q)(P,Q)LL 是离 PP 更近的直径中点,RR 是离 QQ 更近的直径中点,然后对于每一个关键点 uu,如果它离 PP 更远,那么删除 uLu \to L 路径上的点会对答案有贡献,若它离 QQ 更远,那么删除 uRu \to R 路径上的点会对答案有贡献,若同样远,显然此时直径的中点一定在点上,那么删除 uCu \to C 路径上的点会对答案有贡献。

    可以用树上差分维护每个点删除后的贡献。

    #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
    上传者