noip2012 圖論+貪心+二分

首先,這道題是有二分性質的,就是說時間放的越長,能切斷的點就越多。然後就是貪心驗證。
剛開始這樣想的——對於每一次驗證,按軍隊深度(與根之間的距離)從排序後,
1.一個點在給定時間內跑不到第二層,就儘量往上跑。
2.如果跑到第二層,就填上這一層。
3.如果跑到第二層,發現這點填過了,就跑到根,記錄一下剩餘時間。
這樣之後bfs,沒有填過的第2層由跑到根的點補一下。
調了一天,發現思路錯了。
因爲我可以一個點去補其他點,再讓其他點補回來。

4
1 2 3
1 3 2
1 4 7
3
2 2 3

答案應該是9,3號點堵4號,2號堵3號。
正解是如果一隻軍隊可以與自己那條路徑上的那個第二層根節點(A)配對,且這個軍隊如果到達根便無法返回A,那麼就讓他駐紮在A。證:如果這隻軍隊去了別的地方x點,然後y軍隊來填補A,由已知得w(A)>W(x)顯然讓y去x,這支軍隊駐紮在A最好。

#include<iostream>
#include<cstdio>
#include<queue>
#include<ctime>
#include<cstring>
#include<algorithm>
#define maxn 50005
#define LL long long
using namespace std;
int n,m;
struct E{
	int to,nxt;
	LL d;
}b[maxn*2];
int fst[maxn],tot;
void insert(int f,int t,LL d)
{
	b[++tot]=(E){t,fst[f],d};fst[f]=tot;
	b[++tot]=(E){f,fst[t],d};fst[t]=tot;
}
LL dis[maxn];
int fa[maxn][20];
int top[maxn];
int pe[maxn];
bool cmp(int a,int b)
{
	if(dis[a]!=dis[b])
		return dis[a]>dis[b];
	return a<b;
}
bool isf[maxn];
int sz[maxn];
void dfs(int x,int p)
{
	top[x]=p;
	for(int i=1;i<=16;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=fst[x];i;i=b[i].nxt)
	{
		int v=b[i].to;
		if(!fa[v][0])
		{
			fa[v][0]=x;
			dis[v]=dis[x]+b[i].d;
			dfs(v,p);sz[x]++;
		}
	}
	if(!sz[x])isf[x]=true;
}
bool vis[maxn];
int nd[maxn],C;
queue<int> Q;
void bfs(LL mid)
{
	Q.push(1);C=0;vis[1]=1;
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=fst[u];i;i=b[i].nxt)
		{
			int v=b[i].to;
			if(!vis[v])
			{
				if(isf[v])
					nd[++C]=top[v];
				else Q.push(v);
				vis[v]=1;
			}
		}
	}
	sort(nd+1,nd+C+1,cmp);
	C=unique(nd+1,nd+C+1)-nd-1;
}
LL stack[maxn];int Top;
bool check(LL mid)
{
	memset(vis,0,sizeof(vis));Top=0;
	for(int i=1;i<=m;i++)
	{
		int u=pe[i];int v=top[u];
		LL cst=dis[u]-dis[v];
		if(vis[v]||dis[u]+dis[v]<=mid)
		{
			LL p=mid-dis[u];
			stack[++Top]=p;
		}
		else if(cst<=mid)vis[v]=1;
		else
		{
			int p=u;LL res=mid;
			for(int j=16;j>=0;j--)
			{
				int w=fa[p][j];
				LL wv=dis[p]-dis[w];
				if(wv<=res){p=w;res-=wv;}
			}
			while(sz[fa[p][0]]==1&&!vis[p]) 
				vis[p]=1,p=fa[p][0];
			vis[p]=1;
		}
	}
	bfs(mid);
	int H=1;
	for(int i=C;i>=1;i--)
	{
		LL p=dis[nd[i]];
		while(stack[H]<p&&H<=Top)H++;
		if(H>Top) return false;H++;
	}
	return true;
}
int main()
{
	scanf("%d",&n);
	int u,v;LL d;
	int cnt=0;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%lld",&u,&v,&d);
		insert(u,v,d);
		if(u==1||v==1) cnt++;
	}
	for(int i=0;i<=18;i++)fa[1][i]=1;
	for(int i=fst[1];i;i=b[i].nxt)
	{
		int v=b[i].to;
		dis[v]=b[i].d;
		fa[v][0]=1;
		dfs(v,v);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
		scanf("%d",&pe[i]);
	sort(pe+1,pe+m+1,cmp);
	if(m<cnt)
	{
		puts("-1");
		return 0;
	}
	LL l=-1,r=1e9,mid;
	while(r-l>1)
	{
		mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid;
	}
	cout<<r<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章