[日常训练]普通计算姬

Description

给定一棵n个节点的带权树,节点编号为1-n,以root为根,设sum_{p}表示以点p为根的这棵子树中所有节点的权值和。支持下列两种操作:

1.给定两个整数u,v,修改点u的权值为v
2.给定两个整数l,r,计算$\sum_{i=l}^{r}sum_{i}

Input

第一行两个整数n,m,表示树的节点数与操作次数。
接下来一行n个整数,第i个整数d_{i}表示点i的初始权值。
接下来n行每行两个整数a_{i},b_{i},表示一条树上的边,若a_{i}=0则说明b_{i}是根root
接下来m行每行三个整数,第一个整数op表示操作类型。
op=1则接下来两个整数u,v表示将点u的权值修改为v
op=2则接下来两个整数l,r表示询问$\sum_{i=l}^{r}sum_{i}

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

0$\leq$d_{i},v<2^{31};1$\leq$l$\leq$r$\leq$n;1$\leq$u$\leq$n

Solution

a_{i}分成k(\sqrt{n}$\leq$k$\leq$\sqrt{n}+1)个区间,每个区间的大小为\sqrtn\sqrt{n}。预处理出每个节点iroot路线上的所有节点所属分块j的总数t[i][j]。每次修改时只需修改每个分块的总值。每次询问时,只需算至多k个区间的和,以及至多2\times\sqrt{n}a_{i}

再记录每个节点的dfs序:fro[i]表示开始访问i节点的时间,beh[i]表示结束访问i节点的时间。dfs序对应的值记为key[\;]。每次改变一个节点i的值时,在fro[i]之前(包括其自身)的所有key[i]值都加上v-a_{i}。再对dfs序用类似的方法进行分块,记录每个分块里的节点统一被改变的a_{i}值,记为s[\;]。记fro[i]所属分块为frbeh[i]所属分块为be,则节点i的值为(s[fr]+key[fro[i])-(s[be]+key[beh[i])

时间复杂度:O(n+\sqrt{n}$\times$n)

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 317
#define N 100005
#define M 200005
using namespace std;
typedef unsigned long long ll;
struct graph{
    int nxt,to;
}e[M];
int g[N],f[N],n,m,cnt;
ll a[N];bool v[N];
/*==========================_d:dfs序    _n:编号==========================*/ 
ll tot[N][K]/*每个点对应编号分块个数*/,s_d[K<<1],s_n[K]/*每个分块里的数字和*/,key[N<<1]/*dfs序单个值*/;
int fro[N],beh[N],r_d,t_d=1,r_n,t_n;//s:分块总数,r:分块大小
int n_n[N]/*序号所属分块*/,n_f[N]/*fro所属编号*/,n_b[N]/*beh所属编号*/;
/*===============================read&write===============================*/ 
inline int read(){
    int ret=0;char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c)){
        ret=ret*10+c-'0';
        c=getchar();
    }
    return ret;
}
inline ll read_ll(){
    ll ret=0;char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c)){
        ret=ret*10+c-'0';
        c=getchar();
    }
    return ret;
}
inline void write(ll k){
    if(!k) return;
    write(k/10);
    putchar(k%10+'0'); 
}
/*===============================ini_tree=================================*/ 
inline void addedge(int x,int y){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void dfs(int u){
    int sta[N],top=0;
    cnt=0;v[u]=true;
    for(int i=g[u];i;i=e[i].nxt){
        v[e[i].to]=true;
        sta[++top]=e[i].to;
        ++tot[e[i].to][n_n[e[i].to]];
    }
    while(top){
        u=sta[top];fro[u]=++cnt;
        if(v[e[g[u]].to]){
            beh[u]=++cnt;--top;
            while(f[u]){
                u=sta[top--];beh[u]=++cnt;
            }
        }
        else f[e[g[u]].to]=u;
        for(int i=g[u];i;i=e[i].nxt)
            if(!v[e[i].to]){
                v[e[i].to]=true;
                sta[++top]=e[i].to;
                for(int j=1;j<=t_n;++j)
                    tot[e[i].to][j]=tot[u][j];
                ++tot[e[i].to][n_n[e[i].to]];
            }
    }
}
/*==================================do===================================*/ 
inline void change(int u,ll k){
//    printf("k=%lld\n",k);
    a[u]+=k;
    for(int i=1;i<=t_n;++i)
        s_n[i]+=tot[u][i]*k;
    for(int i=1;i<n_f[u];++i)
        s_d[i]+=k;
    if(n_f[u]*r_d==fro[u])
        s_d[n_f[u]]+=k;
    else for(int i=(n_f[u]-1)*r_d+1;i<=fro[u];++i)
        key[i]+=k;
}
/*=======================================================================*/ 
inline void init(){
/*==================read===================*/ 
    n=read();m=read();
    for(int i=1;i<=n;++i)
        a[i]=read_ll();
    for(int i=1,j,k;i<=n;++i){
        j=read();k=read();
        addedge(j,k);addedge(k,j);
    }
/*================ini_tree=================*/
    r_n=sqrt(n);
    for(int i=1;i<=n;i+=r_n){
        ++t_n;
        for(int j=0;j<r_n&&i+j<=n;++j)
            n_n[i+j]=t_n;
    }
    dfs(0);
    r_d=sqrt(n<<1);t_d=((n<<1)+r_d-1)/r_d;
    for(int i=1;i<=n;++i){
        n_f[i]=(fro[i]+r_d-1)/r_d;
        n_b[i]=(beh[i]+r_d-1)/r_d;
    }
    for(int i=1;i<=n;++i){
        a[0]=a[i];a[i]=0;change(i,a[0]);
    }
/*===================do====================*/
    int op,l,r,u;ll v,ans;
    while(m--){
        op=read();
        if(op==1){
            u=read();v=read_ll();
            change(u,v-a[u]);
        }
        else{
            l=read();r=read();ans=0;
            if(n_n[l]!=n_n[r]){
                for(int i=n_n[l]+1;i<n_n[r];++i)
                    ans+=s_n[i];
                if(l==(n_n[l]-1)*r_n+1) ans+=s_n[n_n[l]];
                else for(int i=n_n[l]*r_n;i>=l;--i)
                    ans+=(s_d[n_f[i]]+key[fro[i]])-(s_d[n_b[i]]+key[beh[i]]);
                if(r==n_n[r]*r_n) ans+=s_n[n_n[r]];
                else for(int i=(n_n[r]-1)*r_n+1;i<=r;++i)
                    ans+=(s_d[n_f[i]]+key[fro[i]])-(s_d[n_b[i]]+key[beh[i]]);
            }
            else{
                if(l==(n_n[l]-1)*r_n+1&&r==n_n[r]*r_n){
                    ans=s_n[n_n[l]];
                }
                else for(int i=l;i<=r;++i)
                    ans+=(s_d[n_f[i]]+key[fro[i]])-(s_d[n_b[i]]+key[beh[i]]);
            }
            if(!ans) putchar('0');
            else write(ans);
            putchar('\n');
        }
    }
}
int main(){
    freopen("common.in","r",stdin);
    freopen("common.out","w",stdout);
    init();
    fclose(stdin);
    fclose(stdout);
    return 0;
}

转载于:https://www.cnblogs.com/AireenYe/p/5831637.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值