Atcoder AGC007E : Shik and Travel(DP)

这篇博客介绍了如何解决Atcoder AGC007E问题,采用二分搜索结合树上的动态规划策略。通过维护子树的状态单调队列,实现O(nlog2n)的时间复杂度解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

题解:
先二分然后在树上DP。

注意到一个子树一定是只进出一次,进去的长度是一个点的深度,出来的长度是另一个点的深度。我们可以对一个点维护一个子树所有这样的状态的单调队列,合并后显然大小为较小子树的大小*2。 时间复杂度 O(nlog2n) O ( n log 2 ⁡ n )

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair <LL,LL> pLL;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}

const int N=2e5+50;
vector <pLL> edge[N];
vector <pLL> f[N];
vector <pLL> tl, tr;
LL lim;
inline void inc(int x,const pLL &t) {
    if(f[x].size() && f[x].back()<=t) return;
    while(f[x].size() && f[x].back().second>=t.second) f[x].pop_back();
    f[x].push_back(t);
}
inline bool dfs(int x,int fa) {
    f[x].clear();
    if(edge[x].size()==1) {f[x].push_back(pLL(0,0)); return true;} 
    LL lc,rc,disl,disr; int fir=0;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e].first; if(v==fa) continue;
        if(!dfs(v,x)) return false;
        if(!fir) fir=1, lc=v, disl=edge[x][e].second;
        else rc=v, disr=edge[x][e].second;
    }
    tl.clear(), tr.clear(); LL lim2=lim-disl-disr;
    if(lim2<0) return false; 
    for(int p1=0,p2=0;p2<f[rc].size();p2++) {
        while(p1<f[lc].size() && f[lc][p1].first+f[rc][p2].second>lim2) ++p1;
        if(p1==f[lc].size()) break;
        tl.push_back(pLL(f[rc][p2].first+disr,f[lc][p1].second+disl));
    }
    for(int p1=0,p2=0;p1<f[lc].size();p1++) {
        while(p2<f[rc].size() && f[lc][p1].second+f[rc][p2].first>lim2) ++p2;
        if(p2==f[rc].size()) break;
        tr.push_back(pLL(f[lc][p1].first+disl,f[rc][p2].second+disr));
    }
    int pl=0, pr=0;
    while(pl<tl.size() && pr<tr.size()) {
        if(tl[pl].first>tr[pr].first) inc(x,tl[pl++]);
        else inc(x,tr[pr++]);
    }
    while(pl<tl.size()) inc(x,tl[pl++]);
    while(pr<tr.size()) inc(x,tr[pr++]);
    return f[x].size()>0;
}
inline bool check(LL v) { lim=v; return dfs(1,0);}
int main() {
    int n=rd();
    for(int i=2;i<=n;i++) {
        int x=rd(), w=rd();
        edge[x].push_back(pLL(i,w));
        edge[i].push_back(pLL(x,w));
    }
    LL l=0, r=1e12, ans=0;
    while(l<=r) {
        LL mid=(l+r)>>1;
        if(check(mid)) ans=mid, r=mid-1;
        else l=mid+1;
    } cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值