【NOIP2019模拟赛】test

【NOIP2019模拟赛】test

题目描述
分析

对于操作2-4,显然是树链剖分的裸题,重点是操作1

首先,操作1显然只对操作3产生影响,假设当前根为root,操作3的节点为u

如果\(u=root\),显然权值之和为整棵树

如果\(u\neq root\)且\(lca(u,root)\neq u\)那么换根操作不对子树权值和产生影响

如果\(u\neq root\)且\(lca(u,root)=u\)那么权值和为整棵树减去\(u\)到\(root\)的链上最靠近\(u\)的一点的子树权值和

代码
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
struct Node
{
    int l,r,val;
}tree[4*N];
struct node
{
    int ver,poi;
}edge[2*N];
int n,m,a[N],deep[N],fa[N],siz[N],maxson,son[N],id[N],val[N],cnt,top[N],root,head[N],tot;

void build(int p,int L,int R)
{
    tree[p].l=L; tree[p].r=R;
    if( L==R )
    {
        tree[p].val=val[L];
        return;
    }
    int mid=(L+R)/2;
    build(p*2,L,mid);
    build(p*2+1,mid+1,R);
    tree[p].val=tree[2*p].val+tree[2*p+1].val;
    return;
}

void change(int p,int x,int val)
{
    if( tree[p].l==tree[p].r )
    {
        tree[p].val=val;
        return;
    }
    int mid=(tree[p].l+tree[p].r)/2;
    if( x <= mid ) change(p*2,x,val);
    else change(p*2+1,x,val);
    tree[p].val=tree[2*p].val+tree[2*p+1].val;
    return;
}

int ask(int p,int L,int R)
{
    if( tree[p].l >= L && tree[p].r <= R ) return tree[p].val;
    int mid=(tree[p].l+tree[p].r)/2;
    int tem=0;
    if( L <= mid ) tem+=ask(p*2,L,R);
    if( R > mid ) tem+=ask(p*2+1,L,R);
    return tem; 
}

void add(int x,int y)
{
    edge[++tot].ver=y;
    edge[tot].poi=head[x];
    head[x]=tot;
    return;
}

void Input()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    return;
}

void dfs1(int x,int f,int dep)
{
    deep[x]=dep;
    fa[x]=f;
    siz[x]=1;
    maxson=-1;
    for(int i=head[x];i;i=edge[i].poi)
    {
        int y=edge[i].ver;
        if( y==fa[x] ) continue;
        dfs1(y,x,dep+1);
        siz[x]+=siz[y];
        if( siz[y] > maxson ) son[x]=y,maxson=siz[y];
    }
    return;
}

void dfs2(int x,int topf)
{
    id[x]=++cnt;
    val[cnt]=a[x];
    top[x]=topf;
    if( son[x]==0 ) return;
    dfs2(son[x],topf);
    for(int i=head[x];i;i=edge[i].poi)
    {
        int y=edge[i].ver;
        if( y==son[x] || y==fa[x] ) continue;
        dfs2(y,y);
    }
    return;
}

void Prepare()
{
    dfs1(1,0,1);
    dfs2(1,1);
    build(1,1,n);
    return;
}

void deal1()
{
    scanf("%d",&root);
    return;
}

void deal2()
{
    int x,y;
    scanf("%d%d",&x,&y);
    change(1,id[x],y);
    return;
}

int check(int x,int y)
{
    if( x==y ) return 0;
    if( deep[x] >= deep[y] ) return -1;
    while( deep[x] < deep[y] )
    {
        if( fa[y]==x ) return y;
        y=fa[y];
    }
    return -1;
}

void deal3()
{
    int x,tem=0;
    scanf("%d",&x);
    if( check(x,root)==0 ) tem=ask(1,1,n);
    else
        if( check(x,root)==-1 ) tem=ask(1,id[x],id[x]+siz[x]-1);
        else tem=ask(1,1,n)-ask(1,id[check(x,root)],id[check(x,root)]+siz[check(x,root)]-1);
    printf("%d\n",tem);
    return; 
}

int chain(int x,int y)
{
    int tem=0;
    while( top[x]!=top[y] )
    {
        if( deep[top[x]] < deep[top[y]] ) swap(x,y);
        tem+=ask(1,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    if( deep[x] < deep[y] ) swap(x,y);
    tem+=ask(1,id[y],id[x]);
    return tem;
}

void deal4()
{
    int x,y,tem;
    scanf("%d%d",&x,&y);
    tem=chain(x,y);
    printf("%d\n",tem);
    return;
}

void work()
{
    for(int i=1;i<=m;i++)
    {
        int que;
        scanf("%d",&que);
        if( que==1 ) deal1();
        else if( que==2 ) deal2();
        else if( que==3 ) deal3();
        else deal4();
    }
    return;
}

int main()
{
    Input();
    Prepare();
    work();
    return 0;
}
上一篇:HDU 1062 Text Reverse —— stack 的使用


下一篇:【LeetCode每天一题】Insertion Sort List(使用插入法对链表进行排序)