树形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线段树……)