IOI2011-Race -点分治

https://www.luogu.org/problemnew/show/P4149

思路

  1. 点分治的整个过程其实就是先找到一个点,把经过他的所有路径(必定dfs)搞完,如果有重复用一下两种形式去重,然后继续分治每一个子树。
  2. 点分治的calc函数有两种形式:一种是把先算整个树的贡献,再减去每个子树内不合法的。我们发现这类问题都有可减性(求和),可以容斥来解;第二种无法容斥是就是一个一个子树的计算,计算完一个子树之后,再用他来更新。点分治的calc函数有两种形式:一种是把先算整个树的贡献,再减去每个子树内不合法的。我们发现这类问题都有可减性(求和),可以容斥来解;第二种无法容斥是就是一个一个子树的计算,计算完一个子树之后,再用他来更新。
  3. 此题为第二类。对于一个分治重心u,我们顺序计算每一个子树的贡献,记录d[i]为之前子树到u的长度为 i 的最小边数。计算时只要一边dfs,一边 a n s = m i n ( a n s , d [ k d i s [ u ] ] + c [ u ] ) ans=min(ans,d[k-dis[u]]+c[u]) 及它的边数和他的另一半的边数之和
  4. 然后我们要用这颗子树更新d[]值,一边dfs,一边更新即可 d [ d i s [ u ] ] = m i n ( d [ d i s [ u ] , c [ u ] ) d[dis[u]]=min(d[dis[u],c[u])
  5. 我们发现现在的d[]值只是针对当前的分治重心u的,所以我们还需要一个dfs,去恢复d[]值,恢复到inf;
  6. 要注意:d一定要赋初值,inf

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
inline int read(){
	char ch=' ';int f=1;int x=0;
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
const int N=200100;
const int M=1000100;
struct node
{
	int v,nxt,w;
}edge[N<<1];
int head[N],cnt;
void add(int u,int v,int w)
{
	cnt++;
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
int sim[N],mxson[N];
int MX,root,smer;
int dis[N],c[N],d[M];
int k,ans;
bool vis[N];
void getroot(int u,int fa)
{
	sim[u]=1;mxson[u]=0;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(vis[v]||v==fa) continue;
		getroot(v,u);
		sim[u]+=sim[v];
		mxson[u]=max(mxson[u],sim[v]);
	}
	mxson[u]=max(mxson[u],smer-sim[u]);
	if(mxson[u]<MX) 
	{
		MX=mxson[u];root=u;
	}
}
void dfs(int u,int fa)
{
	if(dis[u]<=k) ans=min(ans,d[k-dis[u]]+c[u]);
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		int w=edge[i].w;
		if(vis[v]||v==fa) continue;
		dis[v]=dis[u]+w;c[v]=c[u]+1;
		dfs(v,u);
	}
}
void add(int u,int fa)
{
	if(dis[u]<=k) d[dis[u]]=min(d[dis[u]],c[u]);
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(vis[v]||v==fa) continue;
		add(v,u);
	}
}
void recover(int u,int fa)
{
	if(dis[u]<=k) d[dis[u]]=inf;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(vis[v]||v==fa) continue;
		recover(v,u);
	}
}
void divide(int u)
{
	vis[u]=true;d[0]=0;//不加会wa!
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;int w=edge[i].w;
		if(vis[v]) continue;
		dis[v]=w;c[v]=1;
		dfs(v,u);//处理计算v子树
		add(v,u);//更新d
	}
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(vis[v]) continue;
		recover(v,u);//恢复d[]
	}
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(vis[v]) continue;
		smer=sim[v];MX=inf;getroot(v,0);//继续点分治
		divide(root);
	}
}
int main()
{
	int n;
	n=read();k=read();
	int i,j;
	for(i=1;i<n;i++)
	{
		int u,v,w;
		u=read();v=read();w=read();u++;v++;
		add(u,v,w);add(v,u,w);
	}
	memset(d,inf,sizeof(d));
	MX=ans=inf;smer=n;
	getroot(1,0);divide(root);
	printf("%d\n",ans==inf?-1:ans);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_42110318/article/details/86680172