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\) 的颜色分类讨论:
- 若 \(r_2\) 颜色为 \(b\),则 \(l_1\) 和 \(r_2\) 可以匹配,因为 \(l_1\) 优于 \(l_2\) 且 \(r_2\) 优于 \(r_1\),所以无法匹配的 \(l_2\) 和 \(r_1\) 一定不会更优;
- 若 \(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;
}