JZOJ-senior-5956. 【NOIP2018模拟11.7A组】easy LCA

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HuangXinyue1017/article/details/83894338

Time Limits: 1000 ms Memory Limits: 262144 KB

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

输出一行一个整数,表示所求的所有连续子段的权值和。

Sample Input

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

Sample Output

51

Data Constraint

在这里插入图片描述

Solution

结论:区间[l,r]中所有数的LCA=相邻两个数的LCA中深度最浅那个

考虑两点的lca深度能影响到的区间,也就是区间最小值为当前这个数的区间有多少个
这个用单调栈维护一下就好了

Code

#include<algorithm>
#include<cstdio>
#include<cctype>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define ll long long

using namespace std;

const int N=6e5+5,inf=1e9;
int n,num,cnt,a[N],p[N],f[N],g[N],z[N];
int dep[N],last[N],ff[N][21];
struct edge{int to,next;}e[2*N];

inline void read(int &n)
{
	int x=0,w=0; char ch=0;
	while(!isdigit(ch)) w|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	n=w?-x:x;
}

void link(int x,int y)
{
	e[++num]=(edge){y,last[x]},last[x]=num;
}

void dfs(int x,int fa)
{
	dep[x]=dep[fa]+1,ff[x][0]=fa;
	for(int w=last[x];w;w=e[w].next)
		if(e[w].to!=fa) dfs(e[w].to,x);
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	fd(i,20,0) if(dep[ff[x][i]]>=dep[y]) x=ff[x][i];
	if(x==y) return x;
	fd(i,20,0) if(ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i];
	return ff[x][0];
}

int main()
{
	freopen("easy.in","r",stdin);
	freopen("easy.out","w",stdout);
	read(n);
	fo(i,1,n-1)
	{
		int x,y;
		read(x),read(y);
		link(x,y),link(y,x);
	}
	dfs(1,0);
	fo(j,1,20)
		fo(i,1,n)
			ff[i][j]=ff[ff[i][j-1]][j-1];
	ll ans=0;
	fo(i,1,n)
	{
		read(p[i]),ans=(ll)ans+(ll)dep[p[i]];
		if(i>1) a[i-1]=dep[lca(p[i-1],p[i])];
	}
	int m=n-1;
	cnt=0;
	fo(i,1,m)
	{
		while(cnt&&a[i]<=a[z[cnt]]) --cnt;
		f[i]=i-z[cnt];
		z[++cnt]=i;
	}
	a[m+1]=-inf;
	z[cnt=1]=m+1;
	fd(i,m,1)
	{
		while(cnt&&a[i]<a[z[cnt]]) --cnt;
		g[i]=z[cnt]-i;
		z[++cnt]=i;
	}
	fo(i,1,m) ans=ans+(ll)f[i]*(ll)g[i]*(ll)a[i];
	printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/HuangXinyue1017/article/details/83894338