【NOIP2012提高组】疫情控制

25 篇文章 0 订阅
10 篇文章 0 订阅

##Description
给出一颗n个节点有边权的树 和m个队伍所在的位置 队伍从某节点移动到相邻节点要花费边长度的时间 求最少要多少时间使得根节点(编号为1)到每个叶子的路径上最少有一支队伍(根节点不能有队伍)
##Solution
这题第一眼看上去就像树形DP,然而想复杂了。
####二分答案
求什么最大或最小之类的,肯定要用二分。
####小贪心
我们发现每个队伍走得越上,贡献越大。很显然。
####怎么做
既然是越往上越优,那么我们都往上。
现在就分两种情况了:
1、这支队伍到达不了根节点:那么我们就到达他最多能到的节点,然后打一个标记;
2、这支队伍能到达根节点:我们把所有能到达根节点的点统计出来,放在一个数组里面,排一个序,然后我们把根节点没打标记的子节点放在另一个数组里面,排一个序,最后逐个的比较就好了。
注意:如果当前这个队伍从y走到了根节点,然后派到了其他地方,但是别的节点到不了y,这个时候我们可以把当前这支队伍派到y就好了。还要注意,我们标记之后要把整棵树重新标记一下——>如果一个节点的所有子节点都被标记了,那么这个点就要被标记。
####至于在树上跳点
打一个倍增就好了。
##Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
typedef long long ll;
const int maxn=100007;
using namespace std;
ll i,j,k,l,t,n,m,ans,r,mid,yi,er;
ll a[maxn];
ll first[maxn*2],next[maxn*2],last[maxn*2],chang[maxn*2],num;
ll b[maxn],tot,f[maxn][18],g[maxn][18],deep[maxn];
ll c[maxn],d[maxn],e[maxn],wwa,vv;
bool bz[maxn],az[maxn];
struct node{
    ll w,v;
}w[maxn];
struct nod{
    ll a,b;
}v[maxn];
void add(ll x,ll y,ll z){
    last[++num]=y;
    next[num]=first[x];
    first[x]=num;
    chang[num]=z;
}
void dfs(ll x,ll y){
	int i,j,k;
    for(i=first[x];i;i=next[i]){
	    if(last[i]!=y){
	    	f[last[i]][0]=x;
	    	g[last[i]][0]=chang[i];
		    dfs(last[i],x);
		}
	}
}
void suan(ll x,ll y){
    int i,j,k=0;
	if(k+g[x][0]<=y&&f[x][0]){
	    x=f[x][0];
	    k+=g[x][0];
	}
	yi=x;er=k;
}
bool cmp(nod x,nod y){
    return x.a<y.a;
}
bool cmp1(node x,node y){
    return x.w<y.w;
}
void dfs1(ll x){
    bool dz=1,ez=1;
    for(int i=first[x];i;i=next[i]){
	    if(last[i]!=f[x][0]){
		    dfs1(last[i]);
		    dz=0;
		    if(bz[last[i]]==0)ez=0;
		}  
	}
	if(ez&&x!=1&&dz==0)bz[x]=1;
}
bool pan(ll x){
    ll i,j,p,q,o;
    wwa=vv=0;
    memset(bz,0,sizeof(bz));
	fo(i,1,m){
		o=0;
		q=a[i];p=0;
		fod(j,17,0){
	        if(p+g[q][j]<=x&&f[q][j]){
	        	p+=g[q][j];
		        q=f[q][j];
			}
		}
	    if(q!=1){
		    bz[q]=1;
		}
		else{
		    w[++wwa].w=x-p;
			q=a[i];
			fod(j,17,0)if(f[q][j]>1)q=f[q][j];
			w[wwa].v=q;
		}
	}
	dfs1(1);
	sort(w+1,w+1+wwa,cmp1);
	for(i=first[1];i;i=next[i]){
	    if(!bz[last[i]]){
		    v[++vv].a=chang[i];  
		    v[vv].b=last[i];
		}
	}
	sort(v+1,v+1+vv,cmp);
	if(wwa<vv)return 0;
	v[vv+1].a=0x7fffffff;
	k=1;
	fo(i,1,wwa){
	    if(!bz[w[i].v])bz[w[i].v]=1;
	    else if(w[i].w>=v[k].a){
		    bz[v[k].b]=1;
		    k++;
		}
	    while(bz[v[k].b])k++;
	}
	if(k>vv)return 1;
	else return 0;
}
int main(){
	scanf("%lld",&n);
	fo(i,1,n-1){
	    scanf("%lld%lld%lld",&k,&l,&t);
	    add(k,l,t);
	    add(l,k,t);
	}
	dfs(1,0);
	fo(j,1,17){
	    fo(i,1,n){
		    f[i][j]=f[f[i][j-1]][j-1];
		    g[i][j]=g[f[i][j-1]][j-1]+g[i][j-1];
		}
	}
	scanf("%lld",&m);
	fo(i,1,m){
	    scanf("%lld",&a[i]);
	}
	l=0;r=1000000000;
	while(l<r){
	    mid=(l+r)/2;
	    if(pan(mid))r=mid;else l=mid+1;
	}
	printf("%lld\n",l);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值