bzoj 4765: 普通计算姬(分块 dfs序)

4765: 普通计算姬

Time Limit: 30 Sec   Memory Limit: 256 MB
Submit: 1502   Solved: 322
[ Submit][ Status][ Discuss]

Description

"奋战三星期,造台计算机"。小G响应号召,花了三小时造了台普通计算姬。普通计算姬比普通计算机要厉害一些
。普通计算机能计算数列区间和,而普通计算姬能计算树中子树和。更具体地,小G的计算姬可以解决这么个问题
:给定一棵n个节点的带权树,节点编号为1到n,以root为根,设sum[p]表示以点p为根的这棵子树中所有节点的权
值和。计算姬支持下列两种操作:
1 给定两个整数u,v,修改点u的权值为v。
2 给定两个整数l,r,计算sum[l]+sum[l+1]+....+sum[r-1]+sum[r]
尽管计算姬可以很快完成这个问题,可是小G并不知道它的答案是否正确,你能帮助他吗?

Input

第一行两个整数n,m,表示树的节点数与操作次数。
接下来一行n个整数,第i个整数di表示点i的初始权值。
接下来n行每行两个整数ai,bi,表示一条树上的边,若ai=0则说明bi是根。
接下来m行每行三个整数,第一个整数op表示操作类型。
若op=1则接下来两个整数u,v表示将点u的权值修改为v。
若op=2则接下来两个整数l,r表示询问。
N<=10^5,M<=10^5
0<=Di,V<2^31,1<=L<=R<=N,1<=U<=N

Output

对每个操作类型2输出一行一个整数表示答案。

Sample Input

6 4
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5

Sample Output

16
10
9

HINT


这题的操作难点就是 记录每一个节点在每一块中的影响 其他的都是模板


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef unsigned long long LL;
struct node
{
    int l, r, v, f;
}g[N];
struct node1
{
    int to,next;
}p[N*2];
int head[N];
int cnt;
LL a[N], sum[N],ans[N],c[N];
void dfs(int u,int f)
{
    g[u].l=++cnt,g[u].f=f,sum[u]=a[u];
    for(int i=head[u];i!=-1;i=p[i].next)
    {
        int v=p[i].to;
        if(v==f) continue;
        dfs(v,u);
        sum[u]+=sum[v];
    }
    g[u].r=cnt;
    return ;
}
int tmp[1000],ax[N][500];
int lx[N],rx[N],id[N],bx;
void dfs2(int u,int f)
{
    tmp[id[u]]++;
    for(int i=1;i<=bx;i++) ax[u][i]=tmp[i];
    for(int i=head[u];i!=-1;i=p[i].next)
    {
        int v=p[i].to;
        if(v==f) continue;
        dfs2(v,u);
    }
    tmp[id[u]]--;
    return ;
}
void add(int u,LL v)
{
    while(u<=cnt)
    {
        c[u]+=v;
        u+=(u&(-u));
    }
    return ;
}
LL get(int u)
{
    LL hx=0;
    while(u)
    {
        hx+=c[u];
        u-=(u&(-u));
    }
    return hx;
}
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
inline ll readll(){
    ll f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int cny;
void addx(int u,int v)
{
    p[cny].to=v,p[cny].next=head[u];head[u]=cny++;
    p[cny].to=u,p[cny].next=head[v];head[v]=cny++;
    return ;
}
int main()
{
    memset(head,-1,sizeof(head));
    int n, q, x, y;
    scanf("%d %d", &n, &q);
    for(int i=1;i<=n;i++) a[i]=readll();
    int root;
    for(int i=1;i<=n;i++)
    {
        x=read();y=read();
        if(x==0) root=y;
        else addx(x,y);
    }
    cnt=0;
    dfs(root,-1);
    int k=(int)sqrt(1.0*n);
    int i;
    for(i=1;i*k<n;i++)
    {
        lx[i]=(i-1)*k+1,rx[i]=i*k;
        for(int j=lx[i];j<=rx[i];j++) id[j]=i,ans[i]+=sum[j];
    }
    lx[i]=(i-1)*k+1,rx[i]=n;
    bx=i;
    for(int j=lx[i];j<=rx[i];j++) id[j]=i,ans[i]+=sum[j];
    dfs2(root,-1);
    for(int i=1;i<=n;i++) add(g[i].l,a[i]);
    while(q--)
    {
        int x,u,l,r;
        LL v;
        x=Read();
        if(x==1)
        {
            u=read();v=readll();
            for(int i=1;i<=bx;i++) ans[i]+=(LL)(v-a[u])*(ax[u][i]);
            add(g[u].l,v-a[u]);
            a[u]=v;
        }
        else
        {
            l=read();r=read());
            LL hx=0;
            if(id[l]==id[r])
            {
                for(int i=l;i<=r;i++)   hx+=(get(g[i].r)-get(g[i].l-1));
                printf("%llu\n",hx);
            }
            else
            {
                if(l==lx[id[l]]) hx+=ans[id[l]];
                else
                for(int i=l;i<=rx[id[l]];i++)hx+=(get(g[i].r)-get(g[i].l-1));

                if(r==rx[id[r]]) hx+=ans[id[r]];
                else
                for(int i=lx[id[r]];i<=r;i++)hx+=(get(g[i].r)-get(g[i].l-1));

                for(int i=id[l]+1;i<id[r];i++) hx+=ans[i];
                printf("%llu\n",hx);
            }
        }
    }
    return 0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值