Luogu 4149 Race

Luogu 4149 Race

  • 用点分治解决.
  • 点分治在计算路径贡献时,为了不统计在一颗子树中的路径,解决方法一种是容斥,但在这种求最值问题中不便用容斥来撤销.
  • 另一种则是,处理一颗子树时,只考虑前面的子树中产生的贡献,而将当前的子树的节点暂时存储在一个栈中,当前子树处理完毕后,再让栈中的点产生贡献,就可以解决求最值等容斥难以处理的问题.
  • 就本题而言,记 (tmp[i]) 表示路径长度为 (i) 的路径中最少的边数.那么每条路径就可以更新答案 (ans=min{ans,edges+tmp[k-len]}).将当前子树的点存入 (s2) 中,处理完后再用 (s2) 内的路径更新 (tmp).
  • (tmp) 每次要初始化为 (inf) ,为了避免每次遍历所有点,可以记录哪些位置被修改过(记录在 (s1) 中),只用给它们赋值就可以了.
#include<bits/stdc++.h>
#define mp make_pair
#define inf 1e9
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
inline int read()
{
	int out=0,fh=1;
	char jp=getchar();
	while ((jp>'9'||jp<'0')&&jp!='-')
		jp=getchar();
	if (jp=='-')
		fh=-1,jp=getchar();
	while (jp>='0'&&jp<='9')
		out=out*10+jp-'0',jp=getchar();
	return out*fh;
}
const int MAXN=2e5+10;
int ans=inf;
int cnt=0,head[MAXN],to[MAXN<<1],nx[MAXN<<1],val[MAXN<<1];
inline void addedge(int u,int v,int w)
{
	++cnt;
	to[cnt]=v;
	nx[cnt]=head[u];
	val[cnt]=w;
	head[u]=cnt;
}
int n,k;
int vis[MAXN];
int totsiz,sonsiz[MAXN],siz[MAXN],mi,rt;
void findrt(int u,int fa)
{
	siz[u]=1;
	sonsiz[u]=0;
	for(int i=head[u];i;i=nx[i])
		{
			int v=to[i];
			if(v==fa || vis[v])
				continue;
			findrt(v,u);
			siz[u]+=siz[v];
			sonsiz[u]=max(sonsiz[u],siz[v]);
		}
	sonsiz[u]=max(sonsiz[u],totsiz-siz[u]);
	if(sonsiz[u]<mi)
		mi=sonsiz[u],rt=u;
}
int len[MAXN],edges[MAXN];
int tmp[1000000+10];
int s1[MAXN],s2[MAXN];
int tp1,tp2;
void getdis(int u,int fa)
{
	if(k>=len[u])
		ans=min(ans,edges[u]+tmp[k-len[u]]);
	s2[++tp2]=u;
	for(int i=head[u];i;i=nx[i])
		{
			int v=to[i];
			if(v==fa || vis[v])
				continue;
			len[v]=len[u]+val[i];
//			assert(len[v]<=1000000);
			edges[v]=edges[u]+1;
			getdis(v,u);
		}
}
void solve(int u)
{
	len[u]=edges[u]=0;
	for(int i=1;i<=tp1;++i)
		tmp[s1[i]]=inf;
	s1[tp1=1]=u;
	tmp[0]=0;
	for(int i=head[u];i;i=nx[i])
		{
			int v=to[i];
			if(vis[v])
				continue;
			tp2=0;
			len[v]=val[i],edges[v]=1;
			getdis(v,u);
			for(int j=1;j<=tp2;++j)
				{
					v=s2[j];
					tmp[len[v]]=min(tmp[len[v]],edges[v]);
					s1[++tp1]=len[v];
				}
		}
}
void divide(int u)
{
	solve(u);
	vis[u]=1;
	for(int i=head[u];i;i=nx[i])
		{
			int v=to[i];
			if(vis[v])
				continue;
			mi=inf,totsiz=siz[v];
			findrt(v,0);
			divide(rt);
		}
}
int main()
{
//	freopen("testdata.in","r",stdin);
	n=read(),k=read();
	for(int i=1;i<n;++i)
		{
			int u=read()+1,v=read()+1,w=read();
			addedge(u,v,w);
			addedge(v,u,w);
		}	
	for(int i=0;i<=1000000;++i)
		tmp[i]=inf;
	mi=inf,totsiz=n;
	findrt(1,0);
	divide(rt);
	cout<<(ans==inf?-1:ans)<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/jklover/p/10577640.html