【NOIP2012】 疫情控制-二分答案&倍增

传送门:luoguP1084


题解

求最小值首先二分答案,记录每个点倍增向上跳的信息。

设当前二分答案为 m i d mid mid

跳不到根节点的军队的位置是唯一确定的。

对于跳的到的军队结点 i i i取出其到根后仍能多走的距离 r e s 1 = m i d − d i s ( 1 , i ) res1=mid-dis(1,i) res1=middis(1,i)

同时 d f s dfs dfs判断根节点的儿子 c h 1 i ch_{1i} ch1i子树中哪些子树没有被覆盖完,同样记录距离为 r e s 2 = d i s ( 1 , c h 1 i ) res2=dis(1,ch_{1i}) res2=dis(1,ch1i)

r e s 1 , r e s 2 res1,res2 res1,res2都按降序排序,贪心填充即可。

p.s.需要注意:对于某个没有被覆盖完的子树 x x x,显然由自己子树中的走到根有闲余的点来填充最优,所以枚举到 x x x时,先判断 x x x子树中有闲余的且闲余最小的点是否被用过,如果用过不能选再判断当前 r e s 1 m a x res1_{max} res1max是否可用。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+100;
typedef long long ll;

int n,m,d[N],loc[N],ban[N],p[N],ed[N],used[N];
int head[N],nxt[N<<1],to[N<<1],w[N<<1],tot;
ll dis[17][N],ans,val[N];int f[17][N],dr[N];

struct P{
	int id;ll v;
	P(int id_=0,ll v_=0):id(id_),v(v_){};
	bool operator<(const P&ky)const{
	   return v<ky.v;
	}
}A,B;

vector<P>dyd;
vector<P>bxd;

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

void dfs(int x)
{
	ed[x]=1;
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[0][x]) continue;
		d[j]=d[x]+1;f[0][j]=x;dis[0][j]=(ll)w[i];
		ed[x]=0;dfs(j);
	}
}

inline void upp(int id,ll lim)
{
	int i,x=p[id];
	for(i=15;(~i) && lim;--i)
	 if(lim>=dis[i][x] && f[i][x]>1){
	 	lim-=dis[i][x];
	 	x=f[i][x];
	 }
	if(lim>dis[0][x]){
		loc[id]=0;val[id]=lim-dis[0][x];
		dyd.push_back(P(id,val[id]));
		for(i=min(i+1,15);(~i) && f[i][x]>1;x=f[i][x]);
		if(!dr[x] || val[dr[x]]>val[id]) dr[x]=id;
	}else{
		loc[id]=x;ban[x]=1;
	}
}

bool dfck(int x)
{
	if(ed[x]) return true;
	for(int j,i=head[x];i;i=nxt[i]){
		j=to[i];if(j==f[0][x] || ban[j]) continue;
		if(dfck(j)) return true;
	}
	return false;
}

inline bool ck(ll lim)
{
	int i,j;bool re=true;
	for(i=1;i<=m;++i) 
	  upp(i,lim);
	for(i=head[1];i;i=nxt[i]){
		j=to[i];if(ban[j]) continue;
		if(dfck(j)) bxd.push_back(P(j,w[i]));
	}
	sort(bxd.begin(),bxd.end());
	sort(dyd.begin(),dyd.end());
	for(i=bxd.size()-1,j=dyd.size()-1;(~i) && (~j);--i){
	    A=bxd[i];
		if(dr[A.id] && (!used[dr[A.id]])){used[dr[A.id]]=1;continue;}
		for(;(~j) && used[dyd[j].id];--j);
		if(j<0 || A.v>dyd[j].v) break;used[dyd[j].id]=1;
	} 
	if((~i)) re=false;
	for(i=1;i<=m;++i) ban[loc[i]]=used[i]=0;
	dyd.clear();bxd.clear();
	for(i=head[1];i;i=nxt[i]) dr[to[i]]=0;
	return re;
}

int main(){
    int i,j,x,y,z;ll l,r,mid,ori;
    rd(n);
    for(i=1;i<n;++i){
    	rd(x);rd(y);rd(z);ans+=z;
    	lk(x,y,z);lk(y,x,z);
    }
    rd(m);
    for(i=1;i<=m;++i) rd(p[i]);
    dfs(1);
    for(i=1;i<=15;++i)
     for(j=2;j<=n;++j){
     	f[i][j]=f[i-1][f[i-1][j]];
     	dis[i][j]=dis[i-1][j]+dis[i-1][f[i-1][j]];
     }
    l=0;r=ori=ans;ans++;
    for(;l<=r;){
    	mid=(l+r)>>1;
    	if(ck(mid)) r=(ans=mid)-1;
    	else l=mid+1;
    }
    if(ans>ori) ans=-1LL;
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值