洛谷 P2590 树的统计 P3178 树上操作树链剖分入门

Posted niiick

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P2590 树的统计 P3178 树上操作树链剖分入门相关的知识,希望对你有一定的参考价值。

题目描述

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。

我们将以下面的形式来要求你对这棵树完成一些操作:

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

III. QSUM u v: 询问从点u到点v的路径上的节点的权值和

注意:从点u到点v的路径上的节点包括u和v本身

输入格式:

输入文件的第一行为一个整数n,表示节点的个数。

接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。

接下来一行n个整数,第i个整数wi表示节点i的权值。

接下来1行,为一个整数q,表示操作的总数。

接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

输出格式:

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

输入样例

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

输出样例

4
1
2
2
10
6
5
6
5
16
说明

对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。


题目分析:
树链剖分入门
首先树链剖分两个dfs与处理好
然后就直接用预处理出的编号建线段树

对于CHANGE操作
直接在线段树上对num[u]进行单点修改

对于QSUM操作
我们先比较top[u]和top[v]是否相同(即两者是否在同一条重链上)
若不在,我们设dep[u]较大
我们先对ll=num[ top[u] ]到rr=num[u]这段查询区间和,并以ans记录
然后另u=fa[ top[u] ],重复上述操作

若top[u]和top[v]相同
设dep[u] < dep[v]
再次ans+=ll=num[u]到rr=num[u]的区间和
然后返回ans

QMAX操作也是类似

具体解释请看代码注释


#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return f*x;
}

void print(int x)
{
    if(x<0){putchar(‘-‘);x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+‘0‘);
}

int n,t;
int tot;
struct node{int v,nxt;}E[100010];
int head[100010];
int w[100010];
int cnt;    
int dep[100010],fa[100010];
int size[100010],son[100010];
int top[100010],num[100010],pre[100010];
int sum[400010],maxn[400010];
char ss[20];

void add(int u,int v)
{
    E[++tot].v=v;
    E[tot].nxt=head[u];
    head[u]=tot;
}

void dfs1(int u,int pa)
{
    size[u]=1;
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==pa) continue;
        dep[v]=dep[u]+1;  fa[v]=u;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int tp)
{
    num[u]=++cnt; pre[cnt]=u; top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=E[i].nxt)
    {
        int v=E[i].v;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void push(int p)
{
    maxn[p]=max(maxn[p<<1],maxn[p<<1|1]);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}

void build(int s,int t,int p)
{
    if(s==t){ maxn[p]=sum[p]=w[ pre[s] ]; return; }
    int mid=(s+t)>>1;
    build(s,mid,p<<1); build(mid+1,t,p<<1|1);
    push(p);
}

void update(int u,int w,int s,int t,int p)
{
    if(s==t){maxn[p]=sum[p]=w;return;}
    int mid=(s+t)>>1;
    if(u<=mid) update(u,w,s,mid,p<<1);
    else update(u,w,mid+1,t,p<<1|1);
    push(p);
}

int getmax(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return maxn[p];
    int mid=(s+t)>>1;
    int ans=-1e9;
    if(ll<=mid) ans=max(ans, getmax(ll,rr,s,mid,p<<1) );
    if(rr>mid) ans=max(ans, getmax(ll,rr,mid+1,t,p<<1|1) );
    return ans;
}

int qmax(int u,int v)
{
    int ans=-1e9;
    while (top[u]!=top[v])
    {
        if (dep[top[u]]<dep[top[v]])swap(u,v);
        ans=max( ans,getmax(num[top[u]],num[u],1,n,1) );
        u=fa[top[u]];
    }
    if (dep[u]<dep[v])swap(u,v);
    ans=max(ans,getmax(num[v],num[u],1,n,1));
    return ans;
}

int getsum(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return sum[p];
    int mid=(s+t)>>1;
    int ans=0;
    if(ll<=mid) ans+=getsum(ll,rr,s,mid,p<<1) ;
    if(rr>mid) ans+=getsum(ll,rr,mid+1,t,p<<1|1) ;
    return ans;
}

int qsum(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])//若u和v不在一条重链上
    {
        if( dep[ top[u] ] < dep[ top[v] ]) swap(u,v);//取top深度较大的一方
        ans+=getsum(num[top[u]],num[u],1,n,1);//更新u到其top
        u=fa[top[u]];//另u跳到其top的父亲
    }
    if(dep[u]<dep[v]) swap(u,v);//在一条重链上,直接区间更新
    ans+=getsum(num[v],num[u],1,n,1);
    return ans;
}

int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
    w[i]=read();
    
    dep[1]=1; fa[1]=1;
    dfs1(1,-1); dfs2(1,1);
    build(1,n,1);//预处理——树剖
    
    t=read();
    while(t--)
    {
        scanf("%s",&ss);
        int x=read(),y=read();
        if(ss[1]==‘H‘) update(num[x],y,1,n,1);//直接更新num[x]
        else if(ss[1]==‘M‘) print(qmax(x,y)),printf("\n");
        else if(ss[1]==‘S‘) print(qsum(x,y)),printf("\n");
    }
    return 0;
}

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:操作 1 :把某个节点 x 的点权增加 a 。操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入格式:

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

输出格式:

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入样例

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出样例

6
9
13
说明

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不

会超过 10^6 。


题目分析:

这题与上面唯一不同的是要更新u的所有子树
不难发现其实u及其所有子节点
在线段树上的编号
就是ll=num[u]到rr=num[u]+size[u]-1的连续区间
知道这点其实就不难做了


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return x*f;
}

void print(lt x)
{
    if(x<0){putchar(‘-‘);x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+‘0‘);
}

lt n,m;
lt d[100010];
struct node{lt v,nxt;}E[200010];
lt head[100010];
lt cnt,tot;
lt dep[100010],fa[100010],son[100010];
lt top[100010],size[100010];
lt num[100010],pos[100010];
lt sum[1000010],add[1000010];

void adde(lt u,lt v)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    head[u]=tot;
}

void dfs1(lt u,lt pa)
{
    size[u]=1;
    for(lt i=head[u];i;i=E[i].nxt)
    {
        lt v=E[i].v;
        if(v==pa) continue;
        dep[v]=dep[u]+1;  fa[v]=u;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}

void dfs2(lt u,lt tp)
{
    num[u]=++cnt; pos[cnt]=u; top[u]=tp;
    if(son[u]) dfs2(son[u],tp);
    for(lt i=head[u];i;i=E[i].nxt)
    {
        lt v=E[i].v;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void push(lt mid,lt s,lt t,lt p)
{
    add[p<<1]+=add[p]; add[p<<1|1]+=add[p];
    sum[p<<1]+=add[p]*(mid-s+1);
    sum[p<<1|1]+=add[p]*(t-mid);
    add[p]=0;
}

void build(lt s,lt t,lt p)
{
    if(s==t){ sum[p]=d[ pos[s] ]; return; }
    lt mid=(s+t)>>1;
    build(s,mid,p<<1); build(mid+1,t,p<<1|1);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}

void update(lt u,lt w,lt s,lt t,lt p)
{
    if(s==t){sum[p]+=w;return;}
    lt mid=(s+t)>>1;
    if(add[p])push(mid,s,t,p);
    if(u<=mid) update(u,w,s,mid,p<<1);
    else update(u,w,mid+1,t,p<<1|1);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}

void uprange(lt ll,lt rr,lt s,lt t,lt p,lt w)
{
    if(ll<=s&&t<=rr){sum[p]+=(t-s+1)*w;add[p]+=w;return;}
    lt mid=(s+t)>>1;
    if(add[p])push(mid,s,t,p);
    if(ll<=mid)uprange(ll,rr,s,mid,p<<1,w);
    if(rr>mid)uprange(ll,rr,mid+1,t,p<<1|1,w);
    sum[p]=sum[p<<1]+sum[p<<1|1];
}

lt getsum(lt ll,lt rr,lt s,lt t,lt p)
{
    if(ll<=s&&t<=rr) return sum[p];
    lt mid=(s+t)>>1;
    if(add[p])push(mid,s,t,p);
    lt ans=0;
    if(ll<=mid) ans+=getsum(ll,rr,s,mid,p<<1) ;
    if(rr>mid) ans+=getsum(ll,rr,mid+1,t,p<<1|1) ;
    return ans;
}

lt qsum(lt u,lt v)
{
    lt ans=0;
    while(top[u]!=top[v])
    {
        if( dep[ top[u] ] < dep[ top[v] ]) swap(u,v);
        ans+=getsum(num[top[u]],num[u],1,n,1);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    ans+=getsum(num[u],num[v],1,n,1);
    return ans;
}

int main()
{
    n=read();m=read();
    for(lt i=1;i<=n;i++)
    d[i]=read();
    
    for(lt i=1;i<n;i++)
    {
        lt u=read(),v=read();
        adde(u,v);adde(v,u);
    }
    
    dep[1]=1; fa[1]=1;
    dfs1(1,-1); dfs2(1,1);
    build(1,n,1);
    
    while(m--)
    {
        lt k=read(),u=read();
        if(k==1){ lt x=read(); update(num[u],x,1,n,1); }
        else if(k==2) { lt x=read(); uprange(num[u],num[u]+size[u]-1,1,n,1,x); }
        else if(k==3) { print(qsum(u,1)); printf("\n"); }
    }
    return 0;
}

以上是关于洛谷 P2590 树的统计 P3178 树上操作树链剖分入门的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P2590 [ZJOI2008]树的统计

洛谷 P2590 [ZJOI2008]树的统计(树链剖分)

洛谷——P3178 [HAOI2015]树上操作

洛谷P3178 [HAOI2015]树上操作

AC日记——[HAOI2015]树上操作 洛谷 P3178

洛谷 P3178 BZOJ 4034 [HAOI2015]树上操作