dtoj#4258. 铃铛计数问题

Posted jessie-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dtoj#4258. 铃铛计数问题相关的知识,希望对你有一定的参考价值。

题目描述:

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

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

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

算法标签:分块

思路:

分块好题。

比较容易想到分块处理,但是这题要把各个部分分开算。

$cnt[x][i]$ 表示在第 $i$ 个块,有多少个点在 $x$ 到根的链上,那么每次修改 $val[x]$ 就会发生 $cnt[x][i] imes (new-val[x])$ 的权值变化。

对于单个点价值的计算,按照 $dfn$ 序做前缀和,单点修改区间查询,考虑再分一次块,对于每一次修改,对整个块有影响的对于这个块统一加一个值,否则单点加。那么一次查询效率就是 $O(1)$ 的了。

以下代码:

技术图片
#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5,M=320;
LL res[M],sum[N],tag[M],g[N];
int gr[N],sz,num[M][N],dfn[N],ed[N],tot;
int n,Q,val[N],rt,head[N],ne[N<<1],to[N<<1],cnt;
il int read(){
   int x,f=1;char ch;
   _(!)ch==-?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il void ins(int x,int y){
    ne[++cnt]=head[x];
    head[x]=cnt;to[cnt]=y;
}
il void dfs(int x,int fa){
    for(int i=1;i<=gr[n];i++)num[i][x]=num[i][fa];
    num[gr[x]][x]++;dfn[x]=++tot;
    sum[tot]=sum[tot-1]+val[x];g[x]=val[x];
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i])continue;
        dfs(to[i],x);g[x]+=g[to[i]];
    }
    ed[x]=tot;res[gr[x]]+=g[x];
}
il LL cal(int x){
    return sum[ed[x]]-sum[dfn[x]-1]+tag[gr[ed[x]]]-tag[gr[dfn[x]-1]];
}
il void change(int x,int v){
    for(int i=1;i<=gr[n];i++)res[i]+=1ll*v*num[i][x];
    int p=dfn[x];
    for(int i=gr[p]+1;i<=gr[n];i++)tag[i]+=v;
    for(int i=p;i<=min(n,gr[p]*sz);i++)sum[i]+=v;
}
il LL query(int l,int r){
    LL ans=0;
    if(gr[l]==gr[r]){
        for(int i=l;i<=r;i++)ans+=cal(i);
        return ans;
    }
    for(int i=l;i<=gr[l]*sz;i++)ans+=cal(i);
    for(int i=(gr[r]-1)*sz+1;i<=r;i++)ans+=cal(i);
    for(int i=gr[l]+1;i<gr[r];i++)ans+=res[i];
    return ans;
}
int main()
{
    n=read();Q=read();sz=(int)sqrt(n);
    for(int i=1;i<=n;i++)val[i]=read();
    for(int i=1;i<=n;i++)gr[i]=(i-1)/sz+1;
    for(int i=1;i<=n;i++){
        int x=read(),y=read();
        if(x)ins(x,y),ins(y,x);
        else rt=y;
    }
    dfs(rt,0);
    while(Q--){
        int op=read(),l=read(),r=read();
        if(op==1){
            change(l,r-val[l]);val[l]=r;
        }
        else{
            printf("%lld
",query(l,r));
        }
    }
    return 0;
}
View Code

 

以上是关于dtoj#4258. 铃铛计数问题的主要内容,如果未能解决你的问题,请参考以下文章

DTOJ #3160. 序列计数(count)

DTOJ2702:余数

DTOJ2704:数字互换

DTOJ2700:hello world

关于快手小铃铛广告投放的方式

HDU 4258 斜率优化dp