• 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
    ",l);
    	return 0;
    }
    
    Written by Alan_Zhao
  • 相关阅读:
    2. Add Two Numbers
    1. Two Sum
    leetcode 213. 打家劫舍 II JAVA
    leetcode 48. 旋转图像 java
    leetcode 45. 跳跃游戏 II JAVA
    leetcode 42. 接雨水 JAVA
    40. 组合总和 II leetcode JAVA
    24. 两两交换链表中的节点 leetcode
    1002. 查找常用字符 leecode
    leetcode 23. 合并K个排序链表 JAVA
  • 原文地址:https://www.cnblogs.com/alan-zhao-2007/p/p1084-sol.html
Copyright © 2020-2023  润新知