【hdu 6634 Salty Fish】【最小割+長鏈剖分】

題意

給一棵nn個節點的有根樹,點有點權。有mm個攝像機,第ii個攝像機會覆蓋到以xix_i爲根的子樹中距離xix_i不超過kik_i的點,並且可以用cic_i的代價使該攝像機無效。問未被覆蓋點權和-總花費的最大值。
n,m300000n,m\le300000

分析

首先可以最小割建圖:ss往攝像頭連流量爲費用的邊,攝像頭往能覆蓋的點連流量爲inf的邊,每個點往tt連流量爲點權的邊,問題在於如何求最小割。
轉化爲求最大流,如果從葉子到根枚舉攝像頭,用mapmap來維護vi,jv_{i,j}表示以ii爲根的子樹中,深度爲jj的點還能貢獻多少流量,每次優先找深度大的點進行增廣。
用長鏈剖分優化子樹合併,複雜度爲O((n+m)logn)O((n+m)\log n)

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章