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

Description

Description

Input

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

Data Constraint

Solution

  • 这题做法多种多样,什么线段树合并、分治……

  • 我用的方法比较简单好打,用个单调栈就好了。

  • 我们先对相邻两点求出其lca,即: a [ i ] = l c a ( p [ i ] , p [ i + 1 ] ) a[i]=lca(p[i],p[i+1]) a[i]=lca(p[i],p[i+1])

  • 这个我用的是树链剖分求lca,跑得比较快一些。

  • 于是我们就得到了 n − 1 n-1 n1 个点。

  • 易知一段区间的点的lca就是对应的一段 a [ i ] a[i] a[i] 中深度最小的那个。

  • 那么我们对于每个 a [ i ] a[i] a[i] 计算其作为最终lca的答案。

  • (一个 a [ i ] a[i] a[i] 对应着两个点,所以答案最终还要加上 ∑ d e p [ p [ i ] ] \sum dep[p[i]] dep[p[i]]

  • 对于每个 a [ i ] a[i] a[i] ,我们希望求出 l [ i ] l[i] l[i] ,表示以它为lca的一段点最左能扩展到哪里。

  • 于是我们维护一个单调上升 a a a 的栈,遇到一个新的 a [ i ] a[i] a[i] ,就把比 a [ i ] a[i] a[i] 大的都退掉,剩下位置就相当于是 l [ i ] l[i] l[i] 了。

  • 同样的,我们也求出 r [ i ] r[i] r[i] 表示最右到哪儿,注意由于 a [ i ] a[i] a[i] 可能相等,为了不算重,我们可以在算 r [ i ] r[i] r[i] 退栈时把大于改为大于等于,算 l [ i ] l[i] l[i] 仍是大于,这样就解决这个问题了。

  • 那么最后答案就是: ∑ i = 1 n − 1 d e p [ a [ i ] ] ∗ ( i − l [ i ] ) ∗ ( r [ i ] − i ) + ∑ i = 1 n d e p [ p [ i ] ] \sum_{i=1}^{n-1}dep[a[i]]*(i-l[i])*(r[i]-i)+\sum_{i=1}^{n}dep[p[i]] i=1n1dep[a[i]](il[i])(r[i]i)+i=1ndep[p[i]]

  • 时间复杂度 O ( n   l o g   n ) O(n\ log\ n) O(n log n) ,这个 log 是算 lca 时带的,如果用 tarjan 求 lca 可以做到线性(但听说跑得没有树链剖分快)。

Code

#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const int N=6e5+5;
int n,tot;
LL ans;
int first[N],nex[N<<1],en[N<<1];
int dep[N],size[N],son[N],top[N],fa[N];
int p[N],a[N],st[N],l[N],r[N];
inline int read()
{
	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();
	return w?-X:X;
}
inline void insert(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot;
	en[tot]=y;
}
void dfs(int x)
{
	dep[x]=dep[fa[x]]+1;
	size[x]=1;
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x])
		{
			fa[en[i]]=x;
			dfs(en[i]);
			size[x]+=size[en[i]];
			if(size[son[x]]<size[en[i]]) son[x]=en[i];
		}
}
void dfs1(int x,int y)
{
	top[x]=y;
	if(!son[x]) return;
	dfs1(son[x],y);
	for(int i=first[x];i;i=nex[i])
		if(en[i]^fa[x] && en[i]^son[x]) dfs1(en[i],en[i]);
}
inline int lca(int x,int y)
{
	while(top[x]^top[y])
		if(dep[top[x]]>dep[top[y]]) x=fa[top[x]]; else y=fa[top[y]];
	return dep[x]<dep[y]?x:y;
}
int main()
{
	freopen("easy.in","r",stdin);
	freopen("easy.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		insert(x,y);
		insert(y,x);
	}
	dfs(1);
	dfs1(1,1);
	for(int i=1;i<=n;i++)
	{
		p[i]=read();
		ans+=dep[p[i]];
	}
	for(int i=1;i<n;i++) a[i]=dep[lca(p[i],p[i+1])];
	tot=0;
	for(int i=1;i<n;i++)
	{
		while(tot && a[st[tot]]>=a[i]) tot--;
		l[i]=st[tot];
		st[++tot]=i;
	}
	st[tot=0]=n;
	for(int i=n-1;i;i--)
	{
		while(tot && a[st[tot]]>a[i]) tot--;
		r[i]=st[tot];
		st[++tot]=i;
	}
	for(int i=1;i<n;i++) ans+=(LL)a[i]*(i-l[i])*(r[i]-i);
	printf("%lld",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值