cunzai_zsy0531

关注我

P4149 [IOI2011]Race 题解

题面

感觉和板子差不多,但是仔细思考一下就会发现,用两个指针扫的时候很难维护 “不选某个子树”的最小值。这时候需要设计一个排序和贪心方案,使得能够使用模板那种双指针方式解决问题。

首先是几个定义:在对于某个根 \(rt\) 更新答案的时候,设 \(a[]\) 表示子树的所有点,\(b[]\) 表示每个点属于 \(rt\) 的哪一个儿子的子树,\(d[]\) 表示每个点到 \(rt\) 的距离,\(len[]\) 表示每个点到 \(rt\) 经过的边数。

考虑把 \(a\) 按照第一关键字 \(d\) 从小到大,第二关键字 \(len\) 从小到大。那么首先可以确定一件事情:如果匹配同一个点,靠前的一定比靠后的更优。更具体地,如果两个点 \(b,d\) 都相等,那么位置靠后的无论什么情况下都更劣,因为它能匹配到的点,靠前的那个点也能匹配到。根据这个性质,可以直接对整个数组双指针,当 \(d_{a_l}=d_{a_r}\) 时,若 \(d_{a_{r-1}}=d_{a_r}\)--r,否则 ++l。这样的话,如果在 \(l\) 附近和 \(r\) 附近都有 \(d\) 相等的段,看似会使得有些答案跳过去无法计算,但是可以证明这些被跳过去的一定不会更优。具体证明如下:

\(l\) 目前位于 \(l_1\),颜色为 \(a\)\(r\) 目前位于 \(r_1\),颜色为 \(a\)\(l+1\) 位置为 \(l_2\),颜色为 \(b\)\(r-1\) 位置为 \(r_2\)。这些位置的 \(d\) 均相等。

因为 \(b_{l_1}=b_{r_1}\),并且 \(d\) 均相等,所以会导致 --r,也就是说,\(l_2\) 无法和 \(r_1\) 匹配。这时候可以对 \(r_2\) 的颜色分类讨论:

  1. \(r_2\) 颜色为 \(b\),则 \(l_1\)\(r_2\) 可以匹配,因为 \(l_1\) 优于 \(l_2\)\(r_2\) 优于 \(r_1\),所以无法匹配的 \(l_2\)\(r_1\) 一定不会更优;
  2. \(r_2\) 颜色不为 \(b\),则 \(l_2\)\(r_2\) 可以匹配,同理,这样也一定比 \(l_2\)\(r_1\) 更优。

综上所述,这样做所出现的无法匹配的情况都不可能成为最优情况。

这样就可以用这个方法解决这道题了,复杂度因为有排序,所以是 \(O(n\log^2 n)\),常数较小,可以通过此题。

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int max(const int &a,const int &b){return a>b?a:b;}
inline int min(const int &a,const int &b){return a<b?a:b;}
const int N=2e5+13,INF=0x3f3f3f3f;
struct Edge{int v,w,nxt;}e[N<<1];
int n,k,tot,h[N],rt,siz[N],maxson[N],cnt,a[N],b[N],d[N],len[N],ans=INF;
bool vis[N];
inline void add(int u,int v,int w){e[++tot]=(Edge){v,w,h[u]};h[u]=tot;}
void getroot(int u,int fa,int total){
	siz[u]=1,maxson[u]=0;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||vis[v]) continue;
		getroot(v,u,total);
		siz[u]+=siz[v];
		maxson[u]=max(maxson[u],siz[v]);
	}
	maxson[u]=max(maxson[u],total-siz[u]);
	if(!rt||maxson[u]<maxson[rt]) rt=u;
}
void init(int u,int fa,int sum,int top){
	a[++cnt]=u,d[u]=sum,b[u]=top,len[u]=len[fa]+1;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||vis[v]) continue;
		init(v,u,sum+e[i].w,top);
	}
}
inline bool cmp(const int &x,const int &y){return d[x]==d[y]?len[x]<len[y]:d[x]<d[y];}
void solve(int u){
	vis[u]=1;a[cnt=1]=u,b[u]=u,d[u]=len[u]=0;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v]) continue;
		init(v,0,e[i].w,v);
	}
	sort(a+1,a+cnt+1,cmp);
	int l=1,r=cnt;
	while(l<=r){
		if(d[a[l]]+d[a[r]]>k) --r;
		else if(d[a[l]]+d[a[r]]<k) ++l;
		else{
			if(b[a[l]]!=b[a[r]]) ans=min(ans,len[a[l]]+len[a[r]]);
			d[a[r-1]]==d[a[r]]?--r:++l;
		}
	}
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v]) continue;
		rt=0,getroot(v,0,siz[v]);getroot(rt,0,siz[rt]);
		solve(rt);
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,u,v,w;i<n;++i) scanf("%d%d%d",&u,&v,&w),++u,++v,add(u,v,w),add(v,u,w);
	getroot(1,0,n);
	solve(rt);
	if(ans==INF) ans=-1;
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-05-11 18:45  cunzai_zsy0531  阅读(42)  评论(0编辑  收藏  举报