题解:UVA12161 铁人比赛 Ironman Race in Treeland

题解

考虑如果路径必须包含点 \(u\) 怎么做。

\(u\) 为根,求出每个点以代价为边权时的深度 \(D_v\)以收益为边权时的深度 \(L_v\),找到两个点 \(x,y\)

  • \(x,y\) 属于根节点的不同子树;
  • \(D_x+D_y \leq m\)
  • \(L_x+L_y\) 最大。

\((D_x,L_x)\) 表示在坐标平面,可以发现任意一个点右下方的点可以直接删去(代价更大收益更小,肯定不会选)。

所以只需要用一个数据结构(STL)维护横纵坐标都严格递增的点列,并支持对某个 \((D_y,L_y)\) 查询最优的 \((D_x,L_x)\)。由于点列单调,查询的时候二分即可。

考虑完必须包含点 \(u\) 的情况,剩下的部分就是标准的点分治了。

代码

我用了 set<pair<int,int> > 实现,在维护点列的时候一定要想清楚!不然会像我一样错很多

/*
 * @Author: wanggk
 * @Date: 2025-04-05 19:14:18
 */
#include<bits/stdc++.h>
#define For(i,il,ir) for(int i=(il);i<=(ir);++i)
#define Forr(i,ir,il) for(int i=(ir);i>=(il);--i)
#define ForE(u) for(int i=head[u];~i;i=e[i].nxt)
#define Spc putchar(' ')
#define End putchar('\n')
#define mk make_pair
using namespace std;
typedef long long ll;
template<typename T> inline void rd(T& x){ bool f=0;x=0;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); } while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); if(f) x=-x; }
template<typename T,typename... Args> void rd(T& first,Args&... args){ rd(first),rd(args...); }
template<typename T> inline void write(T x){ int write_num[50],len=0; if(x<0) putchar('-'),x=-x; do write_num[len++]=x%10; while(x/=10); while(len--) putchar(write_num[len]+'0'); }
template<typename T,typename... Args> void write(T first,Args... args){ write(first),Spc,write(args...); }
typedef pair<int,int> pii;
const int maxn=3e4+10;
void ckmax(int &x,int y){ x=(y>x?y:x); }

int n,m,res;
int head[maxn],cnt;
struct edge{ int v,nxt,d,l; }e[maxn<<1];
void add(int x,int y,int z1,int z2){ e[cnt]=(edge){y,head[x],z1,z2},head[x]=cnt++; }

int U,rt;
bool vis[maxn];
int c[maxn],sz[maxn];
#define v e[i].v
void getrt(int u,int fa){
    sz[u]=1,c[u]=0;
    ForE(u) if(v^fa && !vis[v]) getrt(v,u),sz[u]+=sz[v],ckmax(c[u],sz[v]);
    ckmax(c[u],U-sz[u]);
    if(rt==-1 || c[u]<c[rt]) rt=u;
}

multiset<pii> st;
void qry(int D,int L){
    auto it=st.lower_bound(mk(m-D+1,0));
    if(it==st.begin()) return;
    ckmax(res,((--it)->second)+L);
}
void upd(int D,int L){
    auto it0=st.lower_bound(mk(D+1,0));
    if(it0!=st.begin() && (--it0)->second >= L) return;

    auto itl=st.lower_bound(mk(D,0)),itr=itl;
    while(itr!=st.end() && (itr->second)<=L) itr++;
    st.erase(itl,itr),st.insert(mk(D,L));
}

int D[maxn],L[maxn],timer;
void dfs(int u,int fa,int sd,int sl){
    D[++timer]=sd,L[timer]=sl;
    if(sd<=m) res=max(res,sl);
    ForE(u) if(v^fa && !vis[v])
        dfs(v,u,sd+e[i].d,sl+e[i].l);
}
void divide(int u)
{
    vis[u]=true;
    ForE(u) if(!vis[v]){
        timer=0,dfs(v,u,e[i].d,e[i].l);
        For(k,1,timer) qry(D[k],L[k]);
        For(k,1,timer) upd(D[k],L[k]);
    }
    st.clear();
    ForE(u) if(!vis[v])
        U=sz[v],rt=-1,getrt(v,u),divide(rt);
}
void solve(){
    rd(n,m); memset(head,-1,sizeof(head));
    for(int i=2,x,y,z,w;i<=n;i++) rd(x,y,z,w),add(x,y,z,w),add(y,x,z,w);
    U=n,rt=-1,getrt(1,0);divide(rt);
}
void clear(){ cnt=res=0; For(i,1,n) vis[i]=false; }
signed main(){
    int T;rd(T);
    For(tt,1,T){ solve(); printf("Case %d: %d\n",tt,res); clear(); }
    return 0;
}
posted @ 2025-04-05 19:44  wanggk  阅读(12)  评论(0)    收藏  举报