Luogu P1084 疫情控制

Luogu P1084 疫情控制


再水篇博客
2019.8.28
騙-1分未遂
打了個二分不會check 勸退

2019.9.29(一個月了啊…)
怎麼說都要打下來
這個題解好哇
註釋了一些在代碼裏

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

#define ll long long

ll ans=-1,sumc=0,l,r;
ll dis[500100][30],path[500100],dep[500100];
int first[500100];
int f[500100][30];
struct nod1{int y,next;ll c;}a[1000010];
struct nod2{int x,old;ll left;}act[500100],wait[500100];
int n,m,army[500100],need=0,can=0,len=0;
int used[500100],v[500100],stay[500100];

void ins(int x,int y,ll c)
{
	len++;
	a[len].y=y;
	a[len].c=c;
	a[len].next=first[x];
	first[x]=len;
}

void pre(int x,int ff)
{
	for(int i=1;i<=25;i++)
	{
		f[x][i]=f[f[x][i-1]][i-1];
		dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
		//這個!!距離的倍增式不要忘加dis[x][i-1]  !!!!!! 
	}
	for(int i=first[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y==ff)continue;
		f[y][0]=x;
		dis[y][0]=a[i].c;
		path[y]=path[x]+a[i].c;
		dep[y]=dep[x]+1;
		pre(y,x);
	}
}

void move(ll nowc)
{
	for(int i=1;i<=m;i++)
	{
		int x=army[i];
		ll went=0;
		for(int j=25;j>=0;j--)
		{
			if(dep[f[x][j]]>1&&(went+dis[x][j]<=nowc))
			{
				went+=dis[x][j];
				x=f[x][j];
			}
		}
		if(f[x][0]==1&&(went+path[x])<=nowc)
		{
			can++;
			act[can].x=x;
			act[can].old=can;//因爲要排序,然而標記(在子樹x中,可以到達根節點,
							//且到根節點後剩餘路程最小的軍隊)是用原編號
							//所以記錄一下原編號 
			act[can].left=nowc-(went+path[x]);
			if(act[can].left<act[stay[x]].left||stay[x]==0)
			{
				stay[x]=can;
			}
		}
		else
		{
			v[x]=1;
		}
	}
}

void find(int x,int ff)
{
	int sum1=0,sum2=0;
	for(int i=first[x];i;i=a[i].next)
	{
		int y=a[i].y;
		if(y==ff)continue;
		sum1++;find(y,x);
		if(v[y]==1)sum2++;
	}
	if(sum1!=0&&sum1==sum2)v[x]=1;
	if(x==1)
	{
		for(int i=first[x];i;i=a[i].next)
		{
			int y=a[i].y;
			if(y==ff)continue;
			if(v[y]==0)
			{
				need++;
				wait[need].left=path[y];
				wait[need].x=y;
			}
		}
	}
}

int cmp(nod2 x,nod2 y)
{
	return x.left>y.left;
}

int check(ll nowc)
{
	for(int i=1;i<=n;i++)
		stay[i]=0,v[i]=0;
	can=0;
	move(nowc);//3.先跳軍隊 
	for(int i=1;i<=can;i++)
		used[i]=0;
	need=0;
	find(1,0);//4.檢查子樹 
	if(can<need)return 0;
	sort(wait+1,wait+1+need,cmp);
	sort(act+1,act+1+can,cmp);
	
	int now=1; 
	for(int i=1;i<=need;i++)//5.分配他們 
	{
		int x=wait[i].x;
		if(used[stay[x]]==0&&stay[x]!=0)
		//這裏!!要判當前要填的子樹 有沒有stay(可以到達根節點,且到根節點後剩餘路程最小的軍隊) 
		{
			used[stay[x]]=1;
			if(stay[x]==act[now].old)now++;
			continue;
		}
		if(act[now].left<wait[i].left)return 0;
		used[act[now].old]=1;now++;
	}
	return 1;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int x,y;ll c;
		scanf("%d %d %lld",&x,&y,&c);
		ins(x,y,c);ins(y,x,c);
		sumc+=c;
	} 
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
		scanf("%d",&army[i]);
	dep[1]=1;
	path[1]=0;
	pre(1,0);//1.先預處理出倍增數組 
	ll l=0,r=sumc;
	while(l<=r)//2.二分 
	{
		ll mid=(l+r)/2;
		if(check(mid))
		{
			ans=mid;
			r=mid-1;
		}else l=mid+1;
	}
	printf("%lld",ans);
}

剛剛溜去看合唱比賽了
我們班天耀中華唱的好好!
雖然我不上臺在臺下有點寂寞
但真的好激動啊 真好
我知道他們練得很刻苦
我也要努力啊!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章