[IOI2011]Race

淀粉质的板子题吧。
考虑处理一颗子树答案时拿其他子树的答案来更新。
具体来说是维护一个桶,记录到分治点距离为\(s\)的点的最短路径。
要记得用队列来清空,不然复杂度挂飞。

Race
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long

ll n,k;

struct P{
  ll to,next,v;
}e[400005];

ll cnt,head[400005],ans;
ll siz[400005],dp[400005],sum,rt,minn;
ll minc[1000005];
bool vis[400005];

std::queue<int>QWQ;

inline void add(ll x,ll y,ll z){
  e[++cnt].to = y;
  e[cnt].next = head[x];
  e[cnt].v = z;
  head[x] = cnt;
}

inline void find(ll now,ll fa){
  siz[now] = 1;
  for(int i = head[now];i;i = e[i].next){
  	ll v = e[i].to;
  	if(v == fa || vis[v])
  	continue;
  	find(v,now);
  	siz[now] += siz[v];
  }
  dp[now] = std::max(siz[now],sum - siz[now]);
//	std::cout<<now<<" "<<dp[now]<<std::endl;
  if(dp[now] <= minn)
  rt = now,minn = dp[now];
}

inline void gotans(ll u,ll s,ll cnt,ll fa){
//	std::cout<<u<<" "<<s<<" "<<cnt<<std::endl; 
  if(s > k)
  return ;
  ans = std::min(minc[k - s] + cnt,ans);
  for(int i = head[u];i;i = e[i].next){
  	ll v = e[i].to;
  	if(vis[v] || v == fa)
  	continue;
  	gotans(v,s + e[i].v,cnt + 1,u);
  }
}


inline void up(ll u,ll s,ll cnt,ll fa){
//	std::cout<<u<<std::endl;
  if(s > k)
  return ;
  minc[s] = std::min(minc[s],cnt);
  if(minc[s] == cnt)
  QWQ.push(s);
  for(int i = head[u];i;i = e[i].next){
  	ll v = e[i].to;
  	if(vis[v] || v == fa)
  	continue;
  	up(v,s + e[i].v,cnt + 1,u);
  }
}

inline void divide(ll now){
//	std::cout<<now<<" "<<dp[now]<<std::endl;
  vis[now] = 1;
  for(int i = head[now];i;i = e[i].next){
  	ll v = e[i].to;
  	if(vis[v])
  	continue;
  	gotans(v,e[i].v,1,now);
  	up(v,e[i].v,1,now);
  }
  while(!QWQ.empty()){
  	minc[QWQ.front()] = 0x3f3f3f3f;
//		std::cout<<QWQ.front()<<std::endl;
  	QWQ.pop();
  }
  for(int i = head[now];i;i = e[i].next){
  	ll v = e[i].to;
//		std::cout<<v<<std::endl;
  	if(vis[v])
  	continue;
  	sum = minn = siz[v];
//		std::cout<<sum<<" "<<minn<<std::endl;
  	find(v,now);
//		std::cout<<":"<<rt<<std::endl;
  	divide(rt);
  }
}

int main(){
  std::memset(minc,0x3f,sizeof(minc));
  minc[0] = 0;
  scanf("%lld%lld",&n,&k);
  for(int i = 1;i <= n - 1;++i){
  	ll x,y,z;
  	scanf("%lld%lld%lld",&x,&y,&z);
  	add(x,y,z);
  	add(y,x,z);
  }
  sum = minn = n;
  ans = 0x3f3f3f3f;
  find(0,-1);
  divide(rt);
  if(ans != 0x3f3f3f3f)
  std::cout<<ans<<std::endl;
  else
  puts("-1");
}
大可以自信一点。
posted @ 2021-04-20 20:19  fhq_treap  阅读(47)  评论(0编辑  收藏  举报