【2019 Multi-University 6 A】Salty Fish 題解

題目大意

  有一棵 nn 個結點的樹,第 ii 個結點有 aia_i 的收益。
  還有 mm 個攝像頭,第 ii 個攝像頭在 xix_i 這個結點上,能監測它子樹裏所有與 xix_i 距離不超過 kik_i 的結點(距離按邊算),黑掉這個攝像頭的代價是 cic_i。一個結點被任何攝像頭監測着它就不能獲得收益。
  求最大獲益。

  1n,m3×105, 1ai,ci1091 \leq n,m \leq 3 \times 10^5,~1 \leq a_i,c_i \leq 10^9
  多測,n106, m106\sum n \leq 10^6,~\sum m \leq 10^6
  4s

\\
\\
\\

題解

  首先這是個“最大獲利”(學名 最大權閉合圖),可以快速建出模型:左邊 mm 個點表示攝像頭,與源點相連,容量爲 cic_i;右邊 nn 個點表示樹上結點,與匯點相連,容量爲 aia_i,然後每個攝像頭與它監控的所有結點連邊,容量爲 ++\infty。跑最小割即是答案。

  然後這個圖太大了,所以要考慮用貪心模擬網絡流。

  對於每個點上的攝像頭,它的最優方案肯定是先找深度最大的結點來流。因此可以每個點維護一個 set,存這棵子樹下每個深度的剩餘流量。然後攝像頭就貪心地在 set 裏找深度最大的去流。

  然後 set 裏的內容是以深度爲下標的,因此用長鏈剖分來合併,這樣就是 O(nlogn)O(n \log n) 的了。

代碼

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair<int,LL> spr;

const int maxn=3e5+5;
const LL inf=1e18;

int n,m,a[maxn];
vector<int> e[maxn];
vector<pair<int,int>> q[maxn];

int maxdeep[maxn],Lson[maxn],deep[maxn];
void dfs_pre(int k,int last)
{
	Lson[k]=0;
	maxdeep[k]=deep[k]=deep[last]+1;
	for(int go:e[k])
	{
		dfs_pre(go,k);
		if (maxdeep[go]>maxdeep[Lson[k]]) Lson[k]=go;
	}
}

LL ans;
set<spr> S[maxn];
int id[maxn];
void merge(int a,int b)
{
	for(spr pr:S[b])
	{
		set<spr>::iterator it=S[a].upper_bound(make_pair(pr.fi,inf));
		if (it==S[a].begin()) continue;
		it--;
		if (it->fi==pr.fi)
		{
			spr now=*it; S[a].erase(now);
			now.se+=pr.se;
			S[a].insert(now);
		} else S[a].insert(pr);
	}
	S[b].clear();
}
void calc(int k)
{
	for(pair<int,int> qi:q[k])
	{
		while (qi.se>0)
		{
			set<spr>::iterator it=S[id[k]].upper_bound(make_pair(qi.fi+deep[k],inf));
			if (it==S[id[k]].begin()) break;
			it--;
			spr now=*it; S[id[k]].erase(now);
			LL nmin=min(now.se,1ll*qi.se);
			ans-=nmin;
			qi.se-=nmin;
			now.se-=nmin;
			if (now.se>0) S[id[k]].insert(now);
		}
	}
}
void dfs(int k)
{
	if (!Lson[k])
	{
		S[k].insert(make_pair(deep[k],a[k]));
		calc(k);
		return;
	}
	dfs(Lson[k]);
	id[k]=id[Lson[k]];
	S[id[k]].insert(make_pair(deep[k],a[k]));
	for(int go:e[k]) if (go!=Lson[k])
	{
		dfs(go);
		merge(id[k],id[go]);
	}
	calc(k);
}

void Clear()
{
	fo(i,1,n)
	{
		e[i].clear(), q[i].clear(), S[i].clear();
		id[i]=i;
	}
	ans=0;
}

int T;
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d %d",&n,&m);
		Clear();
		fo(i,2,n)
		{
			int x;
			scanf("%d",&x);
			e[x].push_back(i);
		}
		fo(i,1,n) scanf("%d",&a[i]), ans+=a[i];
		fo(i,1,m)
		{
			int x,k,c;
			scanf("%d %d %d",&x,&k,&c);
			q[x].push_back(make_pair(k,c));
		}
		
		dfs_pre(1,0);
		
		dfs(1);
		
		printf("%lld\n",ans);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章