计蒜客 ICPC沈阳网络赛 Ka Chang(树上分块 + 树状数组)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82556547

大致题意:给你一棵有根树,有两种操作,一是把某一层的所有的节点增加一个val,二是输出一个节点所在的子树的和。

普通的dfs序可以支持子树查询,但是不能支持按照深度的修改。对于一个深度,修改一次的复杂度是klogn,k为这个神的的节点个数,可以看出,如果是一个类似菊花图的东西,时间复杂度会爆炸。然后为了解决菊花图下的复杂度问题,我们考虑在修改的时候知识对某一个深度打上一个标记,然后在查询的时候,遍历对应子树的所有深度,用对应深度的点的个数乘以深度的改变量即可。但是这样在遇到链的情况下,同样复杂度退化。

于是我们考虑用分块来综合两种算法。对于同一个点比较多的深度,我们修改的时候对这个深度打上标记,而对于那些点的个数比较少的深度,我们则直接修改。按照套路,我们选取\large \sqrt{n}作为这个分界点,当点数大于\large \sqrt{n}时,对深度打标记,小于的时候直接暴力更新。下面分析一个这个的时间复杂度。对于修改操作,如果是大于\large \sqrt{n}的深度,代价是O(1)的;如果小于\large \sqrt{n}则暴力修改,最坏是\large O(\sqrt{n}\log{n}))的,前面的\large \sqrt{n}是对应深度点的个数,后面是树状数组修改的代价。对于查询操作,首先计算那些点的个数小于\large \sqrt{n}的深度的和,这个直接用树状数组求子树区间的和即可,复杂度O(logn);然后是点的个数大于\large \sqrt{n}的深度的部分,我们枚举所有这些深度,对于深度i,我们用lowerbound和upperbound找到这个子树中深度为i的点的个数,乘上深度i的改变量累加到结果中即可,复杂度最坏也是\large O(\sqrt{n}\log{n}),前一个\large \sqrt{n}表示点的个数大于\large \sqrt{n}的深度的个数,最多不会超过\large \sqrt{n}

综上所述,总的时间复杂度就是\large O(n\sqrt{n}\log{n})的。分块算法很好的把解决两种极端问题的方法结合到了一起。具体见代码:

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;

const int N = 1e5 + 10;
const int inv = 5e8 +4;

std::vector<int> g[N],dep[N],large;
int n,num,q,l[N],r[N];
LL s[N],c[N];

void dfs(int x,int fa,int depth)
{
    l[x]=++num;
    dep[depth].pb(num);
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        if (y==fa) continue;
        dfs(y,x,depth+1);
    }
    r[x]=num;
}

void update(int x,int y)
{
    for(int i=x;i<=n;i+=i&-i)
        c[i]+=y;
}

LL getsum(int x)
{
    LL res=0;
    for(int i=x;i;i-=i&-i)
        res+=c[i];
    return res;
}

int main()
{
    sf(n); sf(q);
    int lim=ceil(sqrt(n));
    for(int i=1;i<n;i++)
    {
        int x,y;
        sf(x); sf(y);
        g[x].pb(y); g[y].pb(x);
    }
    dfs(1,0,0);
    for(int i=0;i<n;i++)
        if (dep[i].size()>lim) large.pb(i);
    while(q--)
    {
        int op,x,y;
        sf(op); sf(x);
        if (op==1)
        {
            sf(y);
            if (dep[x].size()>lim) s[x]+=y;
            else
                for(int i=0;i<dep[x].size();i++)
                    update(dep[x][i],y);
        } else
        {
            LL ans=getsum(r[x])-getsum(l[x]-1);
            for(int i=0;i<large.size();i++)
                ans+=(ub(dep[large[i]].begin(),dep[large[i]].end(),r[x])-
                        lb(dep[large[i]].begin(),dep[large[i]].end(),l[x]))*s[large[i]];
            printf("%lld\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82556547