【NOIP2012】疫情传递

时间:2023-03-10 02:20:05
【NOIP2012】疫情传递

题解

这题是真的烦。。。

越来越心疼2012年的dalao们了【不过好像dalao们都不需要本蒟蒻的心疼2333】

其实这题还有点半懂不懂。。。

所以把洛谷上一个比较好的题解粘过来记忆一下233

1.预处理倍增

我们会发现,离根节点越近的节点,控制的节点更多。所以由贪心的思想,所有的军队都要尽可能地往根节点走。

> ”往上提“类型问题一般使用倍增优化。——xzy神犇

xzy神犇的博客:k-xzy.cf

好大的,那么我们可以dfs一遍,将倍增要用的一些值都处理好(见代码)

2.二分答案

军队可以同时移动,说明我们要控制传染病的时间是军队移动到位时,移动时间最长的军队的移动时间。而我们要求最小值,即要求最大化最小值。

> 二分答案一般用于求最大化最小值,最小化最大值。——jyf神犇

jyf的博客

所以就是二分啦,二分一个答案,事情就会更有方向。

3.”上提“军队

使用倍增的方法将军队在二分出的答案限制内尽力往上”提“,不过不可以到根节点。

4.处理剩余路程

如果当前军队可以到达根节点,那么记录一下它的编号和它到达根节点后还可以走的时间rest。如果这个军队i在根节点的子树x中,那么记录一下子树x的符合这个条件的点中,到根节点后剩余路程最短的点。

如果不可以到达,记录它被”提“到的节点被军队设置了检查点。

5.dfs找未被”封死“的子树

如果一个节点建立了检查点或者它的所有子树都设立了检查点,则说明以这个节点为根的子树已经被“封死”。记录根节点的所有子树中,未被“封死”的子树。

6.军队在子树间转移

将我们已经记录好了的可以到根节点的军队按照剩余路程从大到小排序。

将未被“封死”的子树按照到子树到根节点的距离从大到小排序。

然后依次处理未被“封死”的子树要由哪支军队来管辖。

当然离根节点远的军队由剩余路程大的军队来管辖是吼滴啦,不过缀吼滴还是就由本来就在这棵子树上的军队来管辖。所以我们先查看我们事先记录的(在子树x中,可以到达根节点,且到根节点后剩余路程最小的军队)是否被使用,如果被使用,再看当前没有被使用的军队里剩余路程最大的可否到达这棵子树。

这样我们就可以判断当前二分出的答案是否可行了。

来自某dalao——litble

代码

//by 减维
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<map>
#include<algorithm>
#define ll long long
using namespace std; struct edge{
int to,ne;
ll v;
}e[]; struct res{
int po;
ll v;
}rest1[],rest2[]; ll maxroad,fv[][],mp[],mv[];
int n,m,num1,num2,ecnt,pos[],head[],f[][];
bool used[],vis[]; bool cmp(const res&x,const res&y){return x.v>y.v;} void add(int x,int y,ll z)
{
e[++ecnt].to=y;
e[ecnt].v=z;
e[ecnt].ne=head[x];
head[x]=ecnt;
} void dfs(int x)
{
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x][])return ;
f[dd][]=x;
fv[dd][]=e[i].v;
dfs(dd);
}
} void beizeng()
{
for(int j=;j<=;++j)
for(int i=;i<=n;++i)
{
f[i][j]=f[f[i][j-]][j-];
fv[i][j]=fv[i][j-]+fv[f[i][j-]][j-];
}
} bool find(int x)
{
bool fl=,ok=;
if(vis[x])return ;
for(int i=head[x];i;i=e[i].ne)
{
int dd=e[i].to;
if(dd==f[x][])continue;
ok=;
if(!find(dd)){
fl=;
if(x==)rest2[++num2].po=dd,rest2[num2].v=e[i].v;
else return ;
}
}
if(!ok)return ;
return fl;
} bool check(ll mid)
{
for(int i=;i<=n;++i)vis[i]=mp[i]=;
for(int i=;i<=m;++i)used[i]=;
num1=,num2=;
for(int i=;i<=m;++i)
{
int x=pos[i];ll dis=;
for(int j=;j>=;--j)
if(f[x][j]>&&dis+fv[x][j]<=mid)
dis+=fv[x][j],x=f[x][j];
if(f[x][]==&&dis+fv[x][]<=mid){
rest1[++num1].po=i,rest1[num1].v=mid-dis-fv[x][];
if(!mp[x]||rest1[num1].v<mv[x])
mv[x]=rest1[num1].v,mp[x]=i;
}
else vis[x]=;
}
if(find())return ;
sort(rest1+,rest1+num1+,cmp);
sort(rest2+,rest2+num2+,cmp);
int num=;
used[]=;
for(int i=;i<=num2;++i)
{
if(!used[mp[rest2[i].po]]){used[mp[rest2[i].po]]=;continue;}
while(num<=num1&&(used[rest1[num].po]||rest1[num].v<rest2[i].v))num++;
if(num>num1)return ;
used[rest1[num].po]=;
}
return ;
} int main()
{
scanf("%d",&n);
for(int i=;i<n;++i)
{
int x,y;ll z;
scanf("%d%d%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
maxroad+=z;
}
dfs();
beizeng();
scanf("%d",&m);
for(int i=;i<=m;++i)scanf("%d",&pos[i]);
ll l=,r=maxroad;
while(l<r)
{
ll mid=(l+r)>>;
if(check(mid))r=mid;//,printf("%lld 1\n",mid);
else l=mid+;//,printf("%lld 0\n",mid);
}
printf("%lld",l);
}