LUOGU P1084 疫情控制(二分+贪心+树上倍增)

时间:2023-01-23 19:15:56

传送门

解题思路

  比较神的一道题。首先发现是最小值问题,并且具有单调性,所以要考虑二分答案。其次有一个性质是军队越靠上越优,所以我们要将所有的军队尽量向上提,这一过程我们用倍增实现。发现这时有两种军队,一种是到根之后可以继续移动,另一种不行。那么记录下来那些到根之后可以移动的点和可以继续移动的距离,存到一个结构体里,顺便还要记录下来每个根节点的子节点中剩余路程最短的点。继续一遍\(dfs\),可以求出那些需要被占领的根节点的子节点,然后记录他们到根的距离。把两个数组从大到小排序,贪心的选取剩余路程大于需要占领的子节点到根的距离的那些点去占领,注意也要考虑到不移动的情况。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib> using namespace std;
const int MAXN = 50005;
typedef long long LL; inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
} int n,m,head[MAXN],cnt,to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1],a[MAXN];
int f[MAXN][23],ar[MAXN],resid[MAXN],cntb,cnta;
LL dis[MAXN][23],ans,sum,resmin[MAXN];
bool vis[MAXN],use[MAXN]; struct Data{
int id;LL res;
friend bool operator<(const Data A,const Data B){
return A.res>B.res;
}
}tmp[MAXN],b[MAXN]; inline void add(int bg,int ed,int w){
to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,head[bg]=cnt;
} void dfs(int x,int fa,int w){
f[x][0]=fa;dis[x][0]=w;int u;
for(int i=1;i<=20;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];
// printf("f[%d][%d]=%d\n",x,i,f[x][i]);
}
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==fa) continue;
dfs(u,x,val[i]);
}
} bool dfs2(int x,int fa){
int u;bool flag=false,ok=true;if(vis[x]) return 1;
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==fa) continue;flag=1;
if(!dfs2(u,x)){ok=false;
if(x==1) b[++cntb].id=u,b[cntb].res=val[i];
else return false;
}
}
if(!flag) return false;
return ok;
} inline bool check(LL lim){
cnta=0,cntb=0;int L=1;
memset(use,0,sizeof(use));
memset(vis,0,sizeof(vis));
memset(resmin,0,sizeof(resmin));
memset(resid,0,sizeof(resid));
for(int i=1;i<=m;i++){
ar[i]=a[i];LL now=lim;
for(int j=20;j>=0;j--)
if(f[ar[i]][j]>1 && now>=dis[ar[i]][j]) now-=dis[ar[i]][j],ar[i]=f[ar[i]][j];
if(f[ar[i]][0]==1 && now-dis[ar[i]][0]>0) {
tmp[++cnta].res=now-dis[ar[i]][0];tmp[cnta].id=i;
if(tmp[cnta].res<resmin[ar[i]] || !resmin[ar[i]])
resmin[ar[i]]=tmp[cnta].res,resid[ar[i]]=i;
}
else vis[ar[i]]=1;
}
dfs2(1,0);if(!cntb) return true;
sort(tmp+1,tmp+1+cnta);sort(b+1,b+1+cntb);use[0]=1;
for(int i=1;i<=cntb;i++){
if(!use[resid[b[i].id]]) {use[resid[b[i].id]]=1;continue;}
while(L<=cnta && (tmp[L].res<b[i].res || use[tmp[L].id])) L++;
if(L>cnta) return false;use[tmp[L].id]=1;L++;
}
return true;
} int main(){
// freopen("1.in","r",stdin);
n=rd();int x,y,z;
for(int i=1;i<n;i++){
x=rd(),y=rd(),z=rd();
add(x,y,z),add(y,x,z);sum+=z;
}
m=rd();for(int i=1;i<=m;i++) a[i]=rd();
dfs(1,0,0);LL l=0,r=sum,mid;
while(l<=r) {
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}
/*
10
2 1 3
2 3 4
1 4 7
5 1 9
6 1 2
4 7 9
7 8 8
9 8 8
1 10 2
5
2 8 5 4 2
*/