题目
正解
比赛时想到了一个自认为很高明的方法,但是有锅。
如果题目增加限制:监控的范围的包含关系呈树形结构,那我就能AC了。
这题可以
D
P
DP
DP做,设
f
i
,
j
f_{i,j}
fi,j表示
i
i
i节点为根,深度为
j
j
j以上的点都没有选(但是加上了它们的贡献)的最优答案。
转移相当于维护后缀
m
a
x
max
max和前缀区间加。
直接写是
O
(
n
2
)
O(n^2)
O(n2)的,可以用长链剖分或线段树合并来解决。于是时间复杂度就是
O
(
n
lg
n
)
O(n \lg n)
O(nlgn)。
然而有个更加神奇的做法:
首先这题暴力是可以直接上网络流的。我们发现这个建图方式很优美,于是考虑优化(模拟)一下网络流。
从叶子结点往上做,对于一条流入树中的边,我们希望它尽量先将树中深度大的点流出去的边填满。因为对于更上面的入边中,树中深度小的点比深度大的点更有机会被选到。这样就可以减少浪费,使得流量尽量大。
于是这样设
f
i
,
j
f_{i,j}
fi,j表示节点
i
i
i的子树中,深度为
j
j
j的位置的出边还有多少容量。
每次枚举到一条入边的时候,尽量自底向上填出边的容量。
回溯的时候,子树的
f
i
f_i
fi需要合并。用map
启发式合并,或者长链剖分就可以了。
代码
网络流做法:
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define N 500010
#define ll long long
int n,m;
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int fa[N],dep[N];
int v[N];
struct Info{
int d,c;
};
bool cmph(Info a,Info b){return a.d>b.d;}
vector<Info> h[N];
map<int,ll> f[N];
void merge(map<int,ll> &a,map<int,ll> &b){
if (a.size()<b.size()){
for (auto p=a.begin();p!=a.end();++p)
b[p->first]+=p->second;
a.clear();
swap(a,b);
}
else{
for (auto p=b.begin();p!=b.end();++p)
a[p->first]+=p->second;
b.clear();
}
}
ll flow=0;
void dfs(int x){
dep[x]=dep[fa[x]]+1;
for (EDGE *ei=last[x];ei;ei=ei->las)
dfs(ei->to);
f[x][dep[x]]+=v[x];
for (EDGE *ei=last[x];ei;ei=ei->las)
merge(f[x],f[ei->to]);
for (auto p=h[x].begin();p!=h[x].end() && !f[x].empty();++p){
auto q=f[x].upper_bound(dep[x]+p->d);
if (q==f[x].begin())
continue;
--q;
for (auto tmp=q;q!=f[x].begin() && p->c;tmp=q,--q,f[x].erase(tmp)){
if (p->c<q->second){
flow+=p->c;
q->second-=p->c;
p->c=0;
break;
}
flow+=q->second;
p->c-=q->second;
q->second=0;
}
if (p->c){
if (p->c<q->second){
flow+=p->c;
q->second-=p->c;
p->c=0;
}
else{
flow+=q->second;
p->c-=q->second;
q->second=0;
f[x].erase(q);
}
}
}
}
int main(){
freopen("elysium.in","r",stdin);
freopen("elysium.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=2;i<=n;++i){
scanf("%d",&fa[i]);
e[ne]={i,last[fa[i]]};
last[fa[i]]=e+ne++;
}
ll sum=0;
for (int i=1;i<=n;++i)
scanf("%d",&v[i]),sum+=v[i];
for (int i=1;i<=m;++i){
int x,d,c;
scanf("%d%d%d",&x,&d,&c);
h[x].push_back({d,c});
}
for (int i=1;i<=n;++i)
if (!h[i].empty())
sort(h[i].begin(),h[i].end(),cmph);
dfs(1);
printf("%lld\n",sum-flow);
return 0;
}