題目
正解
比賽時想到了一個自認爲很高明的方法,但是有鍋。
如果題目增加限制:監控的範圍的包含關係呈樹形結構,那我就能AC了。
這題可以做,設表示節點爲根,深度爲以上的點都沒有選(但是加上了它們的貢獻)的最優答案。
轉移相當於維護後綴和前綴區間加。
直接寫是的,可以用長鏈剖分或線段樹合併來解決。於是時間複雜度就是。
然而有個更加神奇的做法:
首先這題暴力是可以直接上網絡流的。我們發現這個建圖方式很優美,於是考慮優化(模擬)一下網絡流。
從葉子結點往上做,對於一條流入樹中的邊,我們希望它儘量先將樹中深度大的點流出去的邊填滿。因爲對於更上面的入邊中,樹中深度小的點比深度大的點更有機會被選到。這樣就可以減少浪費,使得流量儘量大。
於是這樣設表示節點的子樹中,深度爲的位置的出邊還有多少容量。
每次枚舉到一條入邊的時候,儘量自底向上填出邊的容量。
回溯的時候,子樹的需要合併。用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;
}