树形dp/启发式合并Atcoder1272/AGC007E

Posted psychicboom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树形dp/启发式合并Atcoder1272/AGC007E相关的知识,希望对你有一定的参考价值。

考场上把这题丢去翻译了一下,然后自闭了……

考完以后:嗯?有每条边最多经过两次的限制吗??日文题面写得很清楚的样子 然而我看的英文题面


按照套路先二分一个\(ans\)

每条边最多只能经过两次,意味着必须遍历叶节点时,必须先遍历完同一棵子树的叶节点,考虑从下往上做

然后就是一个快乐的树形DP:设\(f[x][a][b]\)表示遍历\(x\)的子树时,由\(x\)到遍历到的第一个叶节点的距离为\(a\),由最后一个被遍历到的叶节点到\(x\)的距离为\(b\)

\[f[x][a][b]=(f[lson][a][l1]\&f[rson][l2][b]\&[l1+val_lson+l2+val_rson\leq ans])|(f[lson][l3][b]\&f[rson][a][l4]\&[l3+val_lson+l4+val_rson\leq ans])\]

其中\(val\)表示边权

注意到对于每个\(a\),只用保留\(b\)最小的状态,对\(b\)也是同理

所以状态数只有\(O(nlogn)\)

用vector存状态,转移的时候启发式合并

我也不是很明白这么转移复杂度为什么是对的……

    #include <bits/stdc++.h>
    #define N 231080
    #define ll long long
    #define For(i,x,y) for(int i=(x);i<=(y);++i)
    #define Rof(i,x,y) for(int i=(x);i>=(y);--i)
    #define Edge(x) for(int i=head[x];i;i=e[i].nxt)
    #define pii pair<ll,ll>
    #define mp make_pair
    #define fr first
    #define sd second
    #define mcpy(x,y) memcpy(x,y,sizeof(x))
    #define mset(x,y) memset(x,y,sizeof(x))
    using namespace std;
    int n,cnt[N];
    ll mn[N];
    vector<pii> a[N];
    vector<int> g[N];
    bool cmp1(const pii &x,const pii &y) return x.fr<y.fr; 
    bool cmp2(const pii &x,const pii &y) return x.sd<y.sd; 
    struct ed int v,w; ch[N][2];
    void merge(vector<pii> &c,vector<pii> &aa,vector<pii> &bb,ll val)
        //if(aa.size()<bb.size()) aa.swap(bb);
        sort(aa.begin(),aa.end(),cmp2);
        sort(bb.begin(),bb.end(),cmp1);
        int _a=(int)aa.size(),_b=(int)bb.size();
        int p=_a-1;
        mn[0]=aa[0].fr;
        For(i,1,_a-1) mn[i]=min(mn[i-1],aa[i].fr);
        For(i,0,_b-1)
            while(p>=0 && aa[p].sd+bb[i].fr>val) p--;
            if(p>=0) c.push_back(mp(mn[p],bb[i].sd));
        p=_a-1;
        sort(aa.begin(),aa.end(),cmp1);
        sort(bb.begin(),bb.end(),cmp2);
        mn[0]=aa[0].sd;
        For(i,1,_a-1) mn[i]=min(mn[i-1],aa[i].sd);
        For(i,0,_b-1)
            while(p>=0 && aa[p].fr+bb[i].sd>val) p--;
            if(p>=0) c.push_back(mp(bb[i].fr,mn[p]));
        
    
    void dfs(int x,ll val)
        if(!a[x].empty()) a[x].clear();
        if(!cnt[x]) a[x].push_back(mp(0,0));return; 
        for(auto to:g[x]) dfs(to,val);
        vector<pii> &ls=a[ch[x][0].v];
        vector<pii> &rs=a[ch[x][1].v];
        For(i,0,(int)ls.size()-1)ls[i].fr+=ch[x][0].w,ls[i].sd+=ch[x][0].w;
        For(i,0,(int)rs.size()-1) rs[i].fr+=ch[x][1].w,rs[i].sd+=ch[x][1].w;
        if(ls.size()<rs.size()) swap(ls,rs);
        merge(a[x],ls,rs,val);
        a[ch[x][0].v].clear();
        a[ch[x][1].v].clear();
    
    bool check(ll x)
        dfs(1,x);
        if(a[1].size()==0) return 0;
        return 1;
    
    int main()
        int x,v;ll ans;
        ll l=0,r=0;
        scanf("%d",&n);
        For(i,2,n) scanf("%d%d",&x,&v),ch[x][cnt[x]++]=(ed)i,v,g[x].push_back(i),r+=v;
        ans=r;
        while(l<=r)
            ll mid=(l+r)>>1;
            if(check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        
        printf("%lld\n",ans);
    

以上是关于树形dp/启发式合并Atcoder1272/AGC007E的主要内容,如果未能解决你的问题,请参考以下文章

本周小结(未完工)

AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)

AtCoder abc256全题解(区间合并模板矩阵快速幂优化dp线段树……)

loj516 DP一般看规律(set启发式合并)

Atcoder TypicalDPContest N~T

P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并