Loading

P1084 [NOIP2012 提高组] 疫情控制 - 树、贪心

题解

这篇题解是写给我自己看的,写得很简略,不保证读者能看懂。

大概算是自己做出来的?

首先发现答案有单调性,于是二分答案一下,问题转化成了问军队能否在 \(mid\) 时间内控制住疫情。

显然军队在非根的节点上时,往上走比往下走更优。于是通过对于每个有军队的点,找到深度最浅的祖先节点,满足祖先到军队的距离 \(\leq mid\)

对于所有到不了根节点的军队,我们把它在树上标记一下,然后 dfs 一遍,看看根节点的哪些儿子被完全覆盖了。

有一个比较显然的贪心是,如果一个军队能到根节点,但到了根节点以后,剩下的时间不能让它回到它走过的那个根节点的儿子,并且这个儿子没有被覆盖,那么让这个军队驻扎在那个儿子一定是最优的。

对于剩下的军队,我们贪心地让它们跨过根节点与剩下的儿子进行匹配即可。

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int N=1e5+5;
int n,m,army[N];ll sumw;
vector<pair<int,ll>> G[N];
int fa[N],siz[N],hson[N];ll dep[N];
void HLD1(int u){
	siz[u]=1;
	for(auto i:G[u]){
		int v=i.first;
		if(v==fa[u]) continue;
		fa[v]=u,dep[v]=dep[u]+i.second;
		HLD1(v);siz[u]+=siz[v];
		if(!hson[u]||siz[hson[u]]<siz[v]) hson[u]=v;
	}
}
int top[N],num[N],rk[N],dfx;
void HLD2(int u,int tp){
	top[u]=tp,num[u]=++dfx,rk[dfx]=u;
	if(hson[u]) HLD2(hson[u],top[u]);
	for(const auto& i:G[u]){
		int v=i.first;
		if(v==fa[u]||v==hson[u]) continue;
		HLD2(v,v);
	}
}
int bel[N];
void Color(int u,int col){
	bel[u]=col;
	for(const auto& i:G[u]){
		if(i.first!=fa[u]) Color(i.first,col);
	}
}
ll ps[N];
ll Anc(int u,ll k){
	while(k>0&&u){
		if(dep[u]-dep[fa[top[u]]]<=k){
			k-=dep[u]-dep[fa[top[u]]];
			u=fa[top[u]];
		}else break;
	}
	if(!u) return 1;
	int r=num[u],l=num[top[u]];
	while(l<r){
		int mid=(l+r)>>1;
		if(ps[num[u]]-ps[mid]<=k) r=mid;
		else l=mid+1;
	}
	return rk[l];
}
bool vis[N];
void Dfs(int u){
	if(vis[u]) return;
	bool fail=0;
	for(const auto& i:G[u]){
		int v=i.first;
		if(v==fa[u]) continue;
		vis[u]=1,Dfs(v);if(!vis[v]) fail=1;
	}
	vis[u]&=!fail;
}
bool Check(ll mid){
	memset(vis,0,sizeof(bool)*(n+5));
	static pair<ll,int> vec[N];
	static ll st[N];
	int len=0;
	For(i,1,m){
		int u=Anc(army[i],mid);
		if(u!=1) vis[u]=1;
		else vec[++len]={mid-dep[army[i]],bel[army[i]]};
	}
	Dfs(1);
	if(vis[1]) return 1;
	sort(vec+1,vec+len+1);
	For(i,1,len){
		if(!vis[vec[i].second]&&vec[i].first<=dep[vec[i].second]){
			vis[vec[i].second]=1,vec[i].first=-1;
		}
	}
	Dfs(1);
	if(vis[1]) return 1;
	st[0]=0;
	for(const auto& i:G[1]) if(!vis[i.first]) st[++st[0]]=i.second;
	sort(st+1,st+st[0]+1);
	int j=1;
	for(int i=1;i<=len&&j<=st[0];++i){
		if(vec[i].first<st[j]) continue;
		++j;
	}
	return j>st[0];
}
int main(){
	Read(n);
	int u,v,w;
	For(i,1,n-1){
		Read(u,v,w);sumw+=w;
		G[u].push_back({v,w}),G[v].push_back({u,w});
	}
	Read(m);
	For(i,1,m) Read(army[i]);
	HLD1(1);HLD2(1,1);
	For(i,2,n) ps[i]=ps[i-1]+dep[rk[i]]-dep[fa[rk[i]]];
	for(auto i:G[1]) Color(i.first,i.first);
	ll l=0,r=sumw;
	while(l<r){
		ll mid=(l+r)>>1;
		if(Check(mid)) r=mid;
		else l=mid+1;
	}
	printf("%lld\n",l);
	return 0;
}
posted @ 2021-07-10 12:22  Alan_Zhao_2007  阅读(45)  评论(0编辑  收藏  举报