題意
給一棵個節點的有根樹,點有點權。有個攝像機,第個攝像機會覆蓋到以爲根的子樹中距離不超過的點,並且可以用的代價使該攝像機無效。問未被覆蓋點權和-總花費的最大值。
分析
首先可以最小割建圖:往攝像頭連流量爲費用的邊,攝像頭往能覆蓋的點連流量爲inf的邊,每個點往連流量爲點權的邊,問題在於如何求最小割。
轉化爲求最大流,如果從葉子到根枚舉攝像頭,用來維護表示以爲根的子樹中,深度爲的點還能貢獻多少流量,每次優先找深度大的點進行增廣。
用長鏈剖分優化子樹合併,複雜度爲。
代碼
#include<bits/stdc++.h>
#define pb push_back
typedef long long LL;
const int N=300005;
int n,m,fa[N],dis[N],dep[N],k[N],sz,id[N];
LL ans,a[N],c[N];
std::vector<int> e[N],tra[N];
std::map<int,LL> ma[N];
std::map<int,LL>::iterator it;
void solve(int x)
{
int son=0;
for (auto to:e[x])
{
dep[to]=dep[x]+1;
solve(to);
if (dis[to]>dis[son]) son=to;
}
dis[x]=dis[son]+1;
id[x]=son?id[son]:++sz;
ma[id[x]][dep[x]]+=a[x];
for (auto to:e[x]) if (to!=son)
for (auto it:ma[id[to]]) ma[id[x]][it.first]+=it.second;
for (auto t:tra[x])
{
it=ma[id[x]].upper_bound(dep[x]+k[t]);
if (it==ma[id[x]].begin()) continue;
it--;
while (c[t])
{
LL w=std::min(c[t],(*it).second);
(*it).second-=w;c[t]-=w;ans-=w;
if (!(*it).second)
{
if (it==ma[id[x]].begin()) {ma[id[x]].erase(it);break;}
int tmp=(*it).first;
it--;ma[id[x]].erase(tmp);
}
}
}
}
int main()
{
int T;scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
for (int i=2;i<=n;i++) scanf("%d",&fa[i]),e[fa[i]].pb(i);
ans=0;
for (int i=1;i<=n;i++) scanf("%lld",&a[i]),ans+=a[i];
for (int i=1;i<=m;i++)
{
int x;scanf("%d%d%lld",&x,&k[i],&c[i]);
tra[x].pb(i);
}
solve(1);
printf("%lld\n",ans);
for (int i=1;i<=n;i++) e[i].clear(),tra[i].clear();
while (sz) ma[sz--].clear();
}
return 0;
}