2020牛客多校第7场C-A National Pandemic[树链剖分+思维]

题目大意


在这里插入图片描述


1.首先我们看一下操作1:实际上可以说成在所有位置上加上 w − d i s t ( x , y ) w-dist(x,y) wdist(x,y),因为 d i s t ( x , x ) = 0 dist(x,x)=0 dist(x,x)=0
2.我们知道 d i s t ( x , y ) = d e p [ x ] + d e p [ y ] − 2 ∗ d e p [ l c a ( x , y ) ] dist(x,y)=dep[x]+dep[y]-2*dep[lca(x,y)] dist(x,y)=dep[x]+dep[y]2dep[lca(x,y)]
3.代入化简 w − d e p [ x ] − d e p [ y ] + 2 ∗ d e p [ l c a ( x , y ) ] w-dep[x]-dep[y]+2*dep[lca(x,y)] wdep[x]dep[y]+2dep[lca(x,y)]
我们将这个式子拆开来看:
(1): w − d e p [ x ] w-dep[x] wdep[x]
(2): d e p [ y ] dep[y] dep[y]
(3): 2 ∗ d e p [ l c a ( x , y ) ] 2*dep[lca(x,y)] 2dep[lca(x,y)]

对于式子1,这是所有点都要加的,我们可以用一个全局变量统计一下 a n s 1 + = w − d e p [ x ] ans1+=w-dep[x] ans1+=wdep[x]
对于式子2是一个定值:为了计算减了几次我们用 n u m num num统计操作1的次数
对于式子3 x 与 所 有 的 y 的 L C A 都 是 x 到 根 节 点 1 【 假 设 的 】 路 径 上 的 点 x与所有的y的LCA都是x到根节点1【假设的】路径上的点 xyLCAx1


如何查询答案呢?
关键是式子3:
这时候我们就要用到树剖去维护了
加上 2 ∗ d e p [ l c a ( x , y ) ] 2*dep[lca(x,y)] 2dep[lca(x,y)]实际上就是相当于给 1 − > l c a 的 路 径 上 边 权 加 2 1->lca的路径上边权加2 1>lca2
由于对于所有点的lca都是一条从 1 − > x 1->x 1>x的路径,所有修改就是在 1 − > x 1->x 1>x链上面每个点 + 2 +2 +2


假如你要查询y点的值:就是 a n s 1 − n u m ∗ d e p [ y ] + q u e r y ( 1 , y ) ; ans1-num*dep[y]+query(1,y); ans1numdep[y]+query(1,y);
这里为什么是直接 q u e r y ( 1 , y ) query(1,y) query(1,y)呢?
因为每次我们只是修改中心点到根节点的链上面的信息如果你y点到根节点路径和这个相交的话交点就是LCA,交点下面是0,交点上面是修改过的,所以直接询问query(1,y);


操作2相当于对这个点额外减掉一些值:开个ad数组维护一下就好了


#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <limits.h>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1) 
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define count Count
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 3e5+10, mod = 1e9 + 9;
const long double eps = 1e-5;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
    read(first);
    read(args...);
}
int T;
int n, m;
struct node {
    int to, next;
}e[N];
int head[N],cnt;
//.................................建图
inline void add(int from, int to)
{
    e[cnt] = {to,head[from]};
    head[from] = cnt ++;
}
//..................................
ll fa[N], dep[N], siz[N], son[N]; 
//每个点的父节点,深度,子树大小,重儿子是谁
inline void dfs1(int u, int f)
{
    fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
    int max_size = -1;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == f) continue;
        dfs1(v,u);
        siz[u] += siz[v];
        if(siz[v] > max_size)
        {
            son[u] = v;
            max_size = siz[v];
        }
    }
}

//..................................
ll tim, dfn[N], top[N];//dfs序,重链顶部
inline void dfs2(int u, int t) // 节点重链的顶部
{
    dfn[u] = ++ tim, top[u] = t;
    if(!son[u]) return;
    dfs2(son[u],t);//先跑重链
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].to;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v,v);
    }
}
//....................................线段树
ll tr[N << 2], tag[N << 2];
inline void pushup(int rt)
{
    tr[rt] = (tr[rt << 1] + tr[rt << 1|1]);
}

inline void pushdown(int rt, int l, int r)
{
    if(tag[rt])
    {
        tag[rt << 1] += tag[rt];
        tag[rt << 1|1] += tag[rt];
        tr[rt << 1] = (tr[rt << 1] + 1ll * (mid - l + 1) * tag[rt]);
        tr[rt << 1|1] = (tr[rt << 1|1] + 1ll * (r - mid) * tag[rt]);
        tag[rt] = 0;
    }
}

inline void build(int rt, int l , int r)
{
    if(l == r)
    {
        tr[rt] = 0;
        return;
    }
    build(Lson), build(Rson);
    pushup(rt);
}

inline void modify(int rt, int l, int r, int posl, int posr, int val)
{
    if(posl <= l && r <= posr)
    {
        tag[rt] += val;
        tr[rt] = (tr[rt] + 1ll * (r - l + 1) * val);
        return;
    }
    pushdown(rt, l, r);
    if(posl <= mid) modify(Lson,posl,posr,val);
    if(posr > mid) modify(Rson,posl,posr,val);
    pushup(rt);
} 

inline ll query(int rt, int l, int r, int posl, int posr)
{
    if(posl <= l && posr >= r) return tr[rt];
    pushdown(rt,l,r);
    ll res = 0;
    if(posl <= mid) res = (res + query(Lson,posl, posr));
    if(posr > mid) res = (res + query(Rson,posl,posr));
    return res;
}
//....................................树剖链上操作
inline void mchain(int x, int y, ll z)
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        modify(1,1,n,dfn[top[x]],dfn[x],z);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    modify(1,1,n,dfn[x],dfn[y],z);
}

inline ll qchain(int x, int y)
{
    ll res = 0;
     while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        res += query(1,1,n,dfn[top[x]],dfn[x]);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    res += query(1,1,n,dfn[x],dfn[y]);
    return res;
}
//...........................................
ll ad[N];
ll num, ans1;
//............................................
inline void init()
{
    num = ans1 = cnt = tim = 0;
    ms(ad,0), ms(head,-1), ms(tag,0);
    ms(son,0);
    dep[1] = 0; 
}
int main()
{
    read(T);
    while(T --)
    {
        init();
        read(n,m);
        _for(i,0,n-1)
        {
            int l, r;
            read(l,r);
            add(l,r);
            add(r,l);
        }
        dfs1(1,1);
        dfs2(1,1);
        build(1,1,n);
        while(m --)
        {
            int op,x,y;
            read(op);
            if(op == 1)
            {
                read(x,y);
                num ++;
                ans1 += y - dep[x];
                mchain(1,x,2);
            }
            else if(op == 2)
            {
                read(y);
                int v = ans1 - num * dep[y] + qchain(1,y);
                if(v > ad[y]) ad[y] = v;
            }
            else 
            {
               read(y);
               printf("%lld\n",ans1 - num * dep[y] + qchain(1,y)-ad[y]);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值