6715. 【2020.06.11省選模擬】T1 極樂迪斯科

題目


正解

比賽時想到了一個自認爲很高明的方法,但是有鍋。
如果題目增加限制:監控的範圍的包含關係呈樹形結構,那我就能AC了。

這題可以DPDP做,設fi,jf_{i,j}表示ii節點爲根,深度爲jj以上的點都沒有選(但是加上了它們的貢獻)的最優答案。
轉移相當於維護後綴maxmax和前綴區間加。
直接寫是O(n2)O(n^2)的,可以用長鏈剖分或線段樹合併來解決。於是時間複雜度就是O(nlgn)O(n \lg n)

然而有個更加神奇的做法:
首先這題暴力是可以直接上網絡流的。我們發現這個建圖方式很優美,於是考慮優化(模擬)一下網絡流。
從葉子結點往上做,對於一條流入樹中的邊,我們希望它儘量先將樹中深度大的點流出去的邊填滿。因爲對於更上面的入邊中,樹中深度小的點比深度大的點更有機會被選到。這樣就可以減少浪費,使得流量儘量大。
於是這樣設fi,jf_{i,j}表示節點ii的子樹中,深度爲jj的位置的出邊還有多少容量。
每次枚舉到一條入邊的時候,儘量自底向上填出邊的容量。
回溯的時候,子樹的fif_i需要合併。用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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章