铃铛计数问题  

————

圣诞节来了,仓鼠又要来策划活动了,今年仓鼠会在圣诞树上挂上铃铛!  

已知圣诞树有 $ n $ 个节点,并且根节点是固定的。记 $ s_i $ 表示以 $ i $ 为根的子树中,所有节点上铃铛数目的总和。但仓鼠觉得询问 $ s_i $ 太简单了,他决定给定 $ l $ 和 $ r $ ,要你回答 $ \sum s_i , l \leq i \leq r $ 的值。  

但是为了避免有的人一次预处理后一劳永逸,仓鼠在大家答题的过程中还会修改某个节点上灯笼的数量。仓鼠还要去筹备活动,你能帮助他写一个程序帮助实时给出标准答案吗?  

————

输入格式  

第一行为两个整数 $ n, q $ 表示圣诞树的节点数和仓鼠操作的次数。  

第二行 $ n $ 个正整数,第 $ n $ 个数 $ w_i $ 表示初始状态下第 $ i $ 号节点的铃铛数。

接下来 $ n $ 行,第 $ i $ 行两个正整数 $ u_i $ 和 $ v_i $ ,描述一条树上的边,特别地,$ u=0 $ 时,表示 $ v_i $ 为圣诞树的根节点。  

接下来 $ q $ 行,每行三个正整数$ op,l,r $ 描述 $ q $ 组操作。当 $ op=1 $ 时表示将编号为 $ l $ 的节点上的铃铛修改为 $ r $ ;当 $ op=2 $ 时表示询问 $ \sum s_i , l \leq i \leq r $ 的值。  

————

输出格式  

对于每组询问操作,你需要依据当前圣诞树情况输出该组询问的标准答案,每次询问的答案独占一行。  

————

样例输入  

“`plain

6 6
7 4 3 4 9 1
4 2
0 1
2 1
2 3
5 3
6 5
2 1 3
1 1 1
2 3 6
2 3 5
1 3 5
2 6 6

“`

————

样例输出  

“`plain  

62
28
27
1

“`  

————

数据范围与提示  

对于 $ 25% $ 的数据, $ n,q \leq 300 $ ;  

对于 $ 40% $ 的数据, $ n,q \leq 3000 $ ;  

对于另外 $ 10% $ 的数据,所有操作 $ op=2 $ ;  

对于另外 $ 15% $ 的数据,所有 $ op=2 $ 的操作 $ l=r $ ;  

对于 $ 100% $ 的数据,$ n,q \leq 100000 $ ,每个结点的铃铛数始终不超过 $ 10^9 $ 。    

————

来源  

2019FCS省选模拟 福州一中

————

Sol:  

奇怪的做法,和题解有点不一样。  

考虑按编号大小分块。  

记 $ f_{i,j} $ 表示编号为 $ i $ 的点到根的路径上编号在第 $ j $ 块内的点数,$ d_{i,j} $ 表示路径上在第 $ j $ 块内的点深度最大的那一个, $ s_{i,j} $ 表示与路径上 $ i $ 同块的点中在块内处于前 $ j $ 个的点的个数。  

修改时,首先维护整块信息,把每一块的总和加上 $ val \times f_{x,j} $ 即可。其次,把 $ d_{x,j} $ 加上 $ val $ ,原因一会会讲到。  

对于查询,按照套路我们先把整块的贡献加上,然后考虑旁边不满一块的价值。显然我们必须 $ O(1) $ 做完每一个位置,不然就会快乐 $ TLE $ 了。  

~~而我与题解的默契也就到此为止了~~  

我们会发现由于修改时每个点的价值都只改在了离它最近的块内节点上,考虑把一块里的节点拉出来形成一棵新树(当然不是真的建出来),那我们只要知道每个节点被不相交的子树改到的价值会统计几次就行了。  

这个次数也就是 $ s $ 数组的含义,直接乘起来就行了。  

~~最重要的一点:看清题目,是修改成 $ val $ ,不是加上 $ val $ !!~~  

附代码  

“`cpp
#include<bits/stdc++.h>
#define N 100005
#define L long long
#define I inline
#define _(d) while(d(isdigit(ch=getchar())))
I int R() {int x;char ch;_(!);x=(ch^48);_()x=(x<<3)+(x<<1)+(ch^48);return x;}
int n,m,g,t,q,nx[N<<1],e[N<<1],h[N];
int a[N],b[N],c[N],f[N][335],rt;L v[N<<1],w[335];
void A(int x,int y) {nx[++t]=h[x],h[x]=t,e[t]=y;}
I int D(int x,int j) {return f[x][j]/1000;}
I int S(int x,int j) {return f[x][j]%1000;}
void B(int x,int fr) {
    #define k e[i]
    int y=D(x,b[x]);
    for(int j=1;j<=g;j++)
        f[x][j]+=S(y,j)+(c[x]<=j);
    f[x][b[x]]+=1000*(x-y);
    for(int j=1;j<=m;j++) {
        y=D(x,j);
        for(int i=h[x];i;i=nx[i])
            if(k^fr) f[k][j]+=1000*y;
    }for(int i=h[x];i;i=nx[i]) if(k^fr) B(k,x);
}
I void V(int x,int aa) {
    for(int j=1,d;j<=m;j++)
        v[d=D(x,j)]+=1ll*aa,w[j]+=1ll*aa*S(d,g);
}
I L Q(int x) {
    L s=0;
    for(int i=std::min(b[x]*g,n);i>(b[x]-1)*g;i–)
        s+=1ll*S(i,c[x])*v[i];return s;
}
I L F(int x,int y) {
    L s=0;for(int i=b[y];i<b[x];i++) s+=w[i];
    return s+Q(x)-Q(y);
}
int main() {
    n=R(),q=R(),g=sqrt(n);
    for(int i=1;i<=n;i++) {
        a[i]=R();b[i]=(i-1)/g+1,c[i]=i-(b[i]-1)*g;
        if(b[i]>m) m=b[i];
    }
    for(int i=1,x,y;i<=n;i++) {
        x=R(),y=R();if(x) A(x,y),A(y,x);else rt=y;
    }B(rt,0);for(int i=1;i<=n;i++) V(i,a[i]);
    for(int op,x,y;q–;) {
        op=R(),x=R(),y=R();
        if(op<2) V(x,y-a[x]),a[x]=y;
        else printf(“%lld\n”,F(y,x-1));}return 0;}

“`

版权声明:本文为adieadie原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/adieadie/p/10581048.html