noip2012-疫情控制(倍增)

啊我又来写这题惹。

还是一样的二分limit emmmm

然后知道了limit判断行不行(ok函数)

先把点都往上提。分成两类点:1.在limit范围内提得到根节点的。2.在limit范围内提不到根节点的。

(这里用倍增上提以优化复杂度,不赘述了)

提不到根节点的肯定是让他留在离根最近的点那边啊(毋庸置疑诶)

然后我们用vis给它标记一下,意思是vis以下的结点都ok的

而对于提的到根节点的军队,我们存一下它的结点号和到了根节点之后还剩下的距离(a数组)

现在我们要检查一下提完后还有啥路径没有被覆盖到的,同样存一下节点号和到根的距离(b数组)注意对于每条路径这里只要找离根只有一条路的距离的点(显然这些点才是最优的)

如果check完了发现所有路径全被覆盖了,直接return 1

否则我们继续……

下面就是结点和军队的配对咯。

刚才忘说了,对于找到的每一个到的了根节点的点,我们倍增完了顺便用他跟新一下结点x的restmin(说白了就是和它在一条路径上就短的军队)

sort一下(大的配对大的,小的配对小的)emmmm这里的sort一定要从大到小不然只有80分。。。因为一定要先满足远的点再满足近的点啊。。。否则不是最优

一个个看b点,先找有没有和它一个路径且没用过的点,如果有直接标注用过并continue

不然将a数组的军队一个个扫描,看有木有能匹配的。如果没有直接返回0

全能匹配就check return 1啦!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
typedef long long ll;
const int maxn=500005;
int cnt=0;int n,m,na,nb;
int u[maxn*2],v[maxn*2],w[maxn*2],head[maxn],nxt[maxn*2];
int p[maxn][35];ll dis[maxn][35];int army[maxn],used[maxn],vis[maxn];
int rest[maxn],restmin[maxn];
struct node
{
	int id,rest;
}a[maxn],b[maxn];//a是军队,b是点 
void add_edge(int x,int y,int z)
{
	cnt++;u[cnt]=x;v[cnt]=y;w[cnt]=z;
	nxt[cnt]=head[x];head[x]=cnt;
}
int cmp(node x,node y)
{
	return x.rest>y.rest;
}
void find(int x,int fa)
{
	p[x][0]=fa;
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		if(v[i]!=fa) 
		{
			dis[v[i]][0]=w[i];
			find(v[i],x);	
		}
	}
}
void prework()
{
	for(int j=1;j<=17;j++)
	{
		for(int i=1;i<=n;i++) 
		{
			p[i][j]=p[p[i][j-1]][j-1];
			dis[i][j]=dis[i][j-1]+dis[p[i][j-1]][j-1];
		}
	}
}
int checkok(int x,int fa)
{
	int fflag=0,flag=1;
	if(vis[x]) return 1;
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		if(v[i]==fa) continue;fflag=1;
		if(!checkok(v[i],x))
		{
			flag=0;
			if(x==1) {b[++nb].id=v[i];b[nb].rest=w[i];}//b是要匹配的点 
			else return 0;
		}
	}
	if(!fflag) return 0;
	return flag;
}
int ok(int limit)
{
	na=nb=0;
	for(int i=1;i<=n;i++) vis[i]=rest[i]=0;
	for(int i=1;i<=m;i++) used[i]=0;
	for(int i=1;i<=m;i++)
	{
		int x=army[i],num=0;
		for(int j=17;j>=0;j--)
		{
			if(p[x][j]>1&&(num+dis[x][j])<=limit)
			{
				num+=dis[x][j];x=p[x][j];
			}
		}
		if(p[x][0]==1&&num+dis[x][0]<=limit)
		{
			a[++na].rest=limit-num-dis[x][0];a[na].id=i;
			if(!rest[x]||a[na].rest<restmin[x]) 
				restmin[x]=a[na].rest,rest[x]=i;
		}
		else vis[x]=1;
	}
	if(checkok(1,0)) return 1;
	sort(a+1,a+1+na,cmp);sort(b+1,b+1+nb,cmp);
	int now=1;used[0]=1;
	for(int i=1;i<=nb;i++)
	{
		if(!used[rest[b[i].id]]) {used[rest[b[i].id]]=1;continue;}
		while(now<=na&&(used[a[now].id]||a[now].rest<b[i].rest)) ++now;//找匹配 
		if(now>na) return 0;used[a[now].id]=1;
	}
	return 1;
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		add_edge(x,y,z);add_edge(y,x,z);
	}
	find(1,0);prework();
	scanf("%d",&m);
	for(int i=1;i<=m;i++) scanf("%d",&army[i]);
	int l=0,r=500001;int ans=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(ok(mid)) {r=mid-1;ans=mid;}
		else l=mid+1;
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mmm040403/article/details/83625805