(第十四届蓝桥杯真题)景区导游

样例输入:

6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1

样例输出:

10 7 13 14

分析:这就是一道lca板子题,lca就是可以在o(logn)的时间复杂度内求解树上两个节点的最近公共祖先的算法,不懂lca的小伙伴可以看这里:最近公共祖先(LCA)(树上倍增)_lca树上倍增_AC__dream的博客-CSDN博客

我们用lca先求出初始序列的花费时间,然后可以发现,如果跳过景点A1,那么我们只需要减去A1到A2的距离即可,如果跳过经典An,那么我们只需要减去An-1到An的距离即可。如果跳过的经典Ai既不是起点也不是终点,那么就需要减去Ai-1到Ai的距离和Ai到Ai+1的距离,然后还需要加上Ai-1到Ai+1的距离,这样就可以枚举答案了,那么对于m次删点操作无非就转化为m次lca求解。

细节见代码:

#include
#include
#include
#include
#include
using namespace std;
const int N=3e5+10;
int fu[N][25],d[N];
long long f[N][25];
int h[N],e[N],ne[N],idx;
long long w[N];
int a[N];
void add(int x,int y,long long z)
{
	e[idx]=y;
	w[idx]=z;
	ne[idx]=h[x];
	h[x]=idx++;
}
void dfs(int x,int fa,long long dis,int hh)
{
	d[x]=hh;
	fu[x][0]=fa;
	f[x][0]=dis;
	for(int i=1;i<=23;i++)
	{
		f[x][i]=f[x][i-1]+f[fu[x][i-1]][i-1];
		fu[x][i]=fu[fu[x][i-1]][i-1];
	}
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==fa) continue;
		dfs(j,x,w[i],hh+1);
	}
}
long long lca(int a,int b)
{
	if(d[a]=0;i--)
	if(d[fu[a][i]]>=d[b])
	{
		ans+=f[a][i];
		a=fu[a][i];
	}
	if(a==b) return ans;
	for(int i=23;i>=0;i--)
	if(fu[a][i]!=fu[b][i])
	{
		ans+=f[a][i]+f[b][i];
		a=fu[a][i];b=fu[b][i];
	}
	ans+=f[a][0]+f[b][0];
	return ans;
}
int main()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		h[i]=-1;
	for(int i=1;i1) ans+=lca(a[i],a[i-1]);
	}
	for(int i=1;i<=k;i++)
	{
		if(i==1) printf("%lld ",ans-lca(a[1],a[2]));
		else if(i==k) printf("%lld ",ans-lca(a[k],a[k-1]));
		else
			printf("%lld ",ans-lca(a[i],a[i-1])-lca(a[i],a[i+1])+lca(a[i-1],a[i+1]));
	}
	return 0;
}

你可能感兴趣的:(蓝桥杯,职场和发展)