NOIP2012 day2 T3 疫情控制

真是一道难题。观察到题目的时间这个量,时间越大显然覆盖的点越多,所以我们说答案具有单调性,这样便可以二分答案了。

接下来最关键的就是如何检验这一个答案,对于一个答案k:

如果有一些点往回移动k的距离仍然不能达到根节点,我们肯定要尽量的把它往上移,这里采用暴力即可。

有些点可以到根。好吧这个时候呢,把还没有控制的根的儿子节点们拿出来,把可以越过根且没有匹配的军队拿出来跑二分图匹配。复杂度O(nmlogn)显然TLE。

我们可以脑补一种贪心的匹配方法。记R[i]表示i这只军队到达根节点后还剩下的时间,B[j]表示j这个根结点的子节点(第二层节点)到根的时间。

那么一只军队能控制一个子节点当且仅当R[i]>=B[j]。这样的话军队移动的时间为k-(R[i]-B[j]),我们的任务是找到一种匹配方法,使得这个式子的最大值最小。

脑补一下便可发现R[i],B[j]越近越好,所以就两个数组排好序匹配啦,下面给出个人认为正确的证明:

如果一只军队不与他本应(指排好序的)那个数配对,那么R[i]-B[j]可能会变小,如果这次的差没变小也会引起别的i的差变小,所以这个式子的最大值变大。

事实真的如此简单吗?不

如果一只军队可以与自己那条路径上的那个第二层根节点(A)配对,且这个军队如果到达根便无法返回A,那么就让他驻扎在A。

证:如果这只军队去了别的地方x点,然后y军队来填补A,由已知得w(A)>W(x)显然让y去x,这支军队驻扎在A最好。

所以如果存在R[i]<B[j]且也能匹配到自己路上的那个点时,取R[i]值最小的匹配。剩下的实现自己去弄吧。感谢Dash的帮助。%%__debug大神。

#include<cstdio>
#include<queue>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define x first
#define y second
#define pii std::pair<long long,int>
const int MAXN=50005*2;
int first[MAXN],next[MAXN],m,Militar[MAXN],n,fa[MAXN],isLeaf[MAXN];
int e=0,v[MAXN],hasf[MAXN],vis[MAXN],bel[MAXN],f[MAXN];
long long ww[MAXN],dis[MAXN],w[MAXN];
bool use[MAXN];
void add(int a,int b,int c)
{
	e++;next[e]=first[a];first[a]=e;ww[e]=c;v[e]=b;
}
bool cmp(const pii& a,const pii& b)
{
	return a.x<b.x;
}
void push_up(int u,int k)
{
	int sum=0;
	vis[u]=1;
	while(1)
	{	
		sum+=w[u];
		if(sum>k)break;
		u=fa[u];if(u==-1)break;
	}
	vis[u]=1;
}
pii sons[MAXN];
pii R[MAXN];
void calc(int u,int fa,int vt)
{
	if(hasf[u])bel[u]=vt;
	for(int i=first[u];i!=-1;i=next[i])
	{
		int to=v[i];
		if(to==fa)continue;
		calc(to,u,vt);
	}
}
bool dfs(int u,int fa)
{
	if(isLeaf[u])return 1;
	for(int i=first[u];i!=-1;i=next[i])
	{
		int to=v[i];
		if(to==fa)continue;
		if(!vis[to])
		{
			 if(dfs(to,u))return 1;
		}
	}
	return 0;
}
void calc_vis(int u,int fa)
{
	int flag=1;
	if(vis[u])return ;
	for(int i=first[u];i!=-1;i=next[i])
	{
		int to=v[i];
		if(to==fa)continue;
		calc_vis(to,u);
		if(!vis[to])flag=0;
	}
	if(!isLeaf[u])vis[u]=flag;
}
void clear()
{
    memset(vis,0,sizeof(vis));
	memset(sons,0,sizeof(sons));
	memset(R,0,sizeof(R));
	memset(f,0,sizeof(f));
	memset(use,0,sizeof(use));
}
int check(long long  k)
{
    clear();
	int o=1,p=1;
	for(int i=1;i<=m;i++)
	{
		if(dis[Militar[i]]>=k)
			push_up(Militar[i],k);
		else R[o].x=k-dis[Militar[i]],R[o++].y=Militar[i];
	}
	calc_vis(1,-1);
	for(int i=first[1];i!=-1;i=next[i])
	{
		int to=v[i];
		if(!vis[to])sons[p].x=w[to],sons[p++].y=to,f[to]=p-1;
	}
	std::sort(R+1,R+o,cmp);
	std::sort(sons+1,sons+p,cmp);
	int a=1,b=1;
	for(int i=1;i<o;i++)
		if(vis[bel[R[i].y]]==0)
			if(R[i].x<dis[bel[R[i].y]])
                vis[bel[R[i].y]]=1,use[i]=1;
	for(int i=1;a<o&&b<p;i++)
	{
        if(use[a]){a++;continue;}
		if(vis[sons[b].y]==0)
		{
		    if(R[a].x>=sons[b].x)vis[sons[b].y]=1,a++,b++;
			else  a++;
		}
		else {b++;continue;}
    }
	return dfs(1,-1);
}
void BFS(int u,int f)
{
	fa[u]=f;
	if(next[first[u]]==-1)isLeaf[u]=1;
	for(int i=first[u];i!=-1;i=next[i])
	{
		int to=v[i];
		if(to==f)continue;
		dis[to]=dis[u]+ww[i],w[to]=ww[i];
		BFS(to,u);	
	}
}
int main()
{
	scanf("%d",&n);
	long long  sum=0;
	int cnt=0;
	for(int i=0;i<=n;i++)first[i]=-1;
	for(int i=1;i<n;i++)
	{
		int a,b;long long c=0;
		scanf("%d %d %I64d",&a,&b,&c);sum+=c;
		add(a,b,c);
		add(b,a,c);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int a;
		scanf("%d",&a);
		Militar[i]=a;
		hasf[a]=1;
	}
	BFS(1,-1);
	long long  l=0,r=sum;
	for(int i=first[1];i!=-1;i=next[i])
		calc(v[i],1,v[i]),cnt++;
	if(m<cnt){printf("-1");return 0;}
	if(!check(0)){printf("0\n");return 0;}
	while(l+1!=r)
	{
		long long  mid=(long long)(l+r)>>1;
		if(!check(mid))
			r=mid;
		else l=mid;
	}
	printf("%I64d\n",r);
}

你可能感兴趣的:(noip)