1. 程式人生 > >noip2012-疫情控制(倍增)

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;
}