牛客练习赛81 D.小 Q 与树(树剖+套路)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛81 D.小 Q 与树(树剖+套路)相关的知识,希望对你有一定的参考价值。
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=1∑nv=1∑nmin(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=1∑k(deeprkk+deeprki−2∗deeplca(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)} =k∗deeprkk+i=1∑kdeeprki−2∗i=1∑kdeeplca(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=1∑kdeeplca(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=1∑kdeeplca(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)