牛客练习赛81 D.小 Q 与树(树剖+套路)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛81 D.小 Q 与树(树剖+套路)相关的知识,希望对你有一定的参考价值。

LINK

n n n个点的树,点 i i i的点权是 a i a_i ai

∑ u = 1 n ∑ v = 1 n m i n ( a u , a v ) ∗ d i s ( u , v ) \\sum\\limits_{u=1}^n\\sum\\limits_{v=1}^n\\rm min(a_u,a_v)*dis(u,v) u=1nv=1nmin(au,av)dis(u,v)


考虑 min ⁡ \\min min不太好处理,我们按照点权从大到小加入

权值第 i i i大的点记作 r k i \\rm rk_i rki

那么这个点和前面所有点产生的贡献是

∑ i = 1 k ( d e e p r k k + d e e p r k i − 2 ∗ d e e p l c a ( r k i , r k k ) \\rm \\sum\\limits_{i=1}^k(deep_{rk_k}+deep_{rk_i}-2*deep_{lca(rk_i,rk_k)} i=1k(deeprkk+deeprki2deeplca(rki,rkk)

= k ∗ d e e p r k k + ∑ i = 1 k d e e p r k i − 2 ∗ ∑ i = 1 k d e e p l c a ( r k i , r k k ) \\rm =k*deep_{rk_k}+\\sum\\limits_{i=1}^kdeep_{rk_i}-2*\\sum\\limits_{i=1}^kdeep_{lca(rk_i,rk_k)} =kdeeprkk+i=1kdeeprki2i=1kdeeplca(rki,rkk)

前两项都可以 O ( 1 ) O(1) O(1)得到,考虑如何求 ∑ i = 1 k d e e p l c a ( r k i , r k k ) \\rm \\sum\\limits_{i=1}^kdeep_{lca(rk_i,rk_k)} i=1kdeeplca(rki,rkk)

这样比较巧妙,考虑 l c a lca lca的深度是两点到根节点的路径交

那么我们每次在树上加入一个点 u u u,就把根节点到 u u u的路径权值加一

现在我们求 ∑ i = 1 k d e e p l c a ( r k i , r k k ) \\rm \\sum\\limits_{i=1}^kdeep_{lca(rk_i,rk_k)} i=1kdeeplca(rki,rkk),只需要查一下根节点到 r k k \\rm rk_k rkk的权值和就好了

这样我们只会查到路径交,而这刚好就是 l c a lca lca的深度和

接下来就简单了,树剖一下即可.

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
const int N = 1e6+10;
const int mod = 998244353;
vector<int>vec[N];
int n,ans,a[N],rk[N];
namespace HLD
{
	long long sum[N],laz[N];
	void push_down(int rt,int l,int r)
	{
		if( laz[rt]==0 )	return;
		sum[ls] += laz[rt]*(mid-l+1), sum[rs] += laz[rt]*(r-mid);
		laz[ls] += laz[rt], laz[rs] += laz[rt], laz[rt] = 0;
	}
	void add(int rt,int l,int r,int L,int R,int val)
	{
		if( l>R || r<L )	return;
		if( l>=L && r<=R ){ laz[rt] += val; sum[rt] += (r-l+1)*val; return; }
		push_down(rt,l,r);
		add( lson,L,R,val ); add( rson,L,R,val );
		sum[rt] = sum[ls]+sum[rs];
	}
	long long ask(int rt,int l,int r,int L,int R)
	{
		if( l>R || r<L )	return 0;
		if( l>=L && r<=R )	return sum[rt];
		push_down(rt,l,r);
		return ask( lson,L,R )+ask( rson,L,R );	
	}
	int dfn[N],siz[N],son[N],fa[N],deep[N],top[N],id;
	void dfs1(int u,int father)
	{
		siz[u] = 1, deep[u] = deep[father]+1, fa[u] = father;
		for( auto v:vec[u] )
		{
			if( v==father )	continue;
			dfs1( v,u );
			siz[u] += siz[v];
			if( siz[son[u]]<siz[v] )	son[u] = v;
		}
	}
	void dfs2(int u,int topf)
	{
		dfn[u] = ++id; top[u] = topf;
		if( son[u] )	dfs2( son[u],topf );
		for( auto v:vec[u] )
		{
			if( v==fa[u] || v==son[u] )	continue;
			dfs2( v,v );
		}
	}
	void Tree_add(int x,int y)
	{
		while( top[x]!=top[y] )
		{
			if( deep[top[x]]<deep[top[y]] )	swap( x,y );
			add(1,1,n,dfn[top[x]],dfn[x],1 );
			x = fa[top[x]];
		}
		if( deep[x]>deep[y] )	swap( x,y );
		add(1,1,n,dfn[x],dfn[y],1 );
	}
	int Tree_ask(int x,int y)
	{
		long long ans = 0;
		while( top[x]!=top[y] )
		{
			if( deep[top[x]]<deep[top[y]] )	swap( x,y );
			ans += ask(1,1,n,dfn[top[x]],dfn[x] );
			x = fa[top[x]];
		}
		if( deep[x]>deep[y] )	swap( x,y );
		ans += ask(1,1,n,dfn[x],dfn[y] )牛客练习赛81 B.小 Q 与彼岸花(Tire树上贪心或区间DP)

牛客练习赛87 D.小G的排列-加强版(思维,组合数学)

牛客练习赛84 D.牛客推荐系统开发之动态特征获取(set应用)

牛客练习赛88 D都市的柏油路太硬

牛客练习赛105 D.点分治分点(spfa&bfs)

牛客练习赛85 D.数学家的迷题(bitset暴力)