题面
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
保证军队不会驻扎在首都。
对于 20%的数据,2≤ n≤ 10;
对于 40%的数据,2 ≤n≤50,0<w <10^5;
对于 60%的数据,2 ≤ n≤1000,0<w <10^6;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10^9。
分析
早知道不是树上差分的题就不做了。。。被别人乱写的Blog误导了TAT
其实和树上差分真没什么关系,更像是树上管理问题,而且是用贪心思想做的那一类。
概括一下题意:在树上放一些点,要让这些点阻断叶子到根的每一条路径,这些但可以移动,要使最长的移动时间最小。
很明显需要二分,直接二分时间。检验在这些时间内是否能够达到条件。
显然军队驻扎的结点层数越小,越有利,所以尽量往上走。而这就产生了两种情况,为优化这个向上走的过程,用到了倍增。
- 不能在规定时间内走到根节点
- 能在规定时间内走到根节点
对于第一种,军队别无他选,就地驻扎当然是最优的,而对于第二种,将待定,因为他们可能在之后被用于翻过根节点驻扎,也可能不翻过根节点驻扎。
此处我们记录下翻过根节点后还能走的距离。如果子树u中有多个点都可以翻越根节点,我们再对于这棵子树记录一个翻过根节点后能走距离最少的点。
现在完成了初步的隔断点布置,这时需要dfs一下求出根的哪些儿子还未被控制。一个点被控制的点需要满足祖先或自己被控制,或者自己的所有子节点被控制。
而对于未被控制的点,就需要用那些能翻过根节点的点尝试控制。
将未被控制的点按离根结点的距离从大到小排序,再将能翻过根节点的点按剩余距离从大到小排序。
首先尝试用子树内能翻过根节点后能走距离最少的点去控制它,再尝试用能翻过的根剩下距离大的点更新里根远的点,剩下距离小的点更新离根近的点。
代码
- #include<bits/stdc++.h>
- using namespace std;
- #define N 50050
- #define RT register
- #define ll long long
- ll n,m,l,r,an,bn,cnt,num,mid,ans=-1;
- ll ok[N],dep[N],use[N],near[N],minx[N],army[N],first[N],fa[N][20],d[N][20];
- struct email
- {
- ll u,v,w;
- ll nxt;
- }e[N*4];
- struct rem
- {
- ll id,dis;
- }a[N],b[N];
- template<class T>
- inline void read(T &x)
- {
- x=0;ll f=1;static char c=getchar();
- while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
- while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
- x*=f;
- }
- bool cmp(rem a,rem b)
- {
- return a.dis>b.dis;
- }
- inline void add(ll u,ll v,ll w)
- {
- e[++cnt].nxt=first[u];first[u]=cnt;
- e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;
- }
- inline void pre(ll u,ll f)
- {
- for(RT ll i=1;(1<<i)<=dep[u];++i)
- {
- fa[u][i]=fa[fa[u][i-1]][i-1];
- d[u][i]=d[u][i-1]+d[fa[u][i-1]][i-1];
- }
- for(RT ll i=first[u];i;i=e[i].nxt)
- {
- ll v=e[i].v,w=e[i].w;
- if(v==f)continue;
- dep[v]=dep[u]+1;
- fa[v][0]=u;d[v][0]=w;
- pre(v,u);
- }
- }
- inline ll dfs(ll u,ll f)
- {
- ll notleaf=0,flag=1;
- if(ok[u])return 1;
- for(RT ll i=first[u];i;i=e[i].nxt)
- {
- ll v=e[i].v,w=e[i].w;
- if(v==f)continue;notleaf=1;
- if(!dfs(v,u))
- {
- flag=0;
- if(u==1)b[++bn].id=v,b[bn].dis=w;
- else return 0;
- }
- }
- if(!notleaf)return 0;
- return flag;
- }
- inline ll check(ll x)
- {
- an=bn=0;
- memset(ok,0,sizeof(ok));
- memset(use,0,sizeof(use));
- memset(near,0,sizeof(near));
- memset(minx,0,sizeof(minx));
- for(RT ll i=1;i<=m;++i)
- {
- ll u=army[i],now=0;
- for(RT ll j=19;j>=0;--j)
- if(fa[u][j]>1&&now+d[u][j]<=x)
- now+=d[u][j],u=fa[u][j];
- if(fa[u][0]==1&&now+d[u][0]<=x)
- {
- a[++an].id=i,a[an].dis=x-now-d[u][0];
- if(!near[u]||a[an].dis<minx[u])
- near[u]=i,minx[u]=a[an].dis;
- }
- else ok[u]=1;
- }
- if(dfs(1,0))return 1;
- sort(a+1,a+1+an,cmp),sort(b+1,b+1+bn,cmp);
- ll now=1;use[0]=1;
- for(RT ll i=1;i<=bn;++i)
- {
- if(!use[near[b[i].id]]){use[near[b[i].id]]=1;continue;}
- while(now<=an&&(use[a[now].id]||a[now].dis<b[i].dis))now++;
- if(now>an)return 0;
- use[a[now].id]=1;
- }
- return 1;
- }
- int main()
- {
- read(n);
- for(RT ll i=1;i<n;++i)
- {
- ll u,v,w;
- read(u),read(v);read(w);
- add(u,v,w);add(v,u,w);
- }
- pre(1,0);
- read(m);
- for(RT ll i=1;i<=m;++i)read(army[i]);
- l=0,r=500050;
- while(l<=r)
- {
- mid=l+r>>1;
- if(check(mid))r=mid-1,ans=mid;
- else l=mid+1;
- }
- printf("%lld",ans);
- return 0;
- }