ACM-ICPC 2018 沈阳赛区网络预赛 J树分块

 J. Ka Chang

Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero point.

Then, you need to handle QQ operations. There're two types:

1\ L\ X1 L X: Increase points by XX of all nodes whose depth equals LL ( the depth of the root is zero ). (x \leq 10^8)(x108)

2\ X2 X: Output sum of all points in the subtree whose root is XX.

Input

Just one case.

The first lines contain two integer, N,QN,Q. (N \leq 10^5, Q \leq 10^5)(N105,Q105).

The next n-1n1 lines: Each line has two integer aa,bb, means that node aa is the father of node bb. It's guaranteed that the input data forms a rooted tree and node 11 is the root of it.

The next QQ lines are queries.

Output

For each query 22, you should output a number means answer.

样例输入复制
3 3
1 2
2 3
1 1 1
2 1
2 3
样例输出复制
1
0
题目来源

ACM-ICPC 2018 沈阳赛区网络预赛

首先这是一个原题,你需要更新深度为x的值,每次仅仅查询子树就可以,用线段树的话,需要更新的太多的

要树分块,打上time标记,每个数据太多不能暴力更新,单独列出来

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
vector<int>G[N];
vector<int>pos[N];
vector<int>vec;
int L[N],R[N];
int tot,n,m,lim=500;
long long s[N],c[N];
void dfs(int now,int deep)
{
    L[now]=++tot;//一块的左标记
    pos[deep].push_back(L[now]);
    for(auto X:G[now])dfs(X,deep+1);
    R[now]=tot;//右标记,这个内全是其子树
    return;
}
void add(int x,int d)
{
    for(;x<=n;x+=x&-x)c[x]+=d;
}
long long sum(int x)
{
    long long ans=0;
    for(;x>0;x-=x&-x)ans+=c[x];
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<n;i++)
        scanf("%d%d",&u,&v),G[u].push_back(v);
    dfs(1,0);
    for(int i=0;i<=n;i++)
        if(pos[i].size()>lim)vec.push_back(i);
    for(int i=0,op,x,y;i<m;i++)
    {
        scanf("%d%d",&op,&x);
        if(op==1)
        {
            scanf("%d",&y);
            if(pos[x].size()<=lim)
                for(auto X:pos[x])add(X,y);//小于所给块大小,直接树状数组更新
            else s[x]+=y;//大于的直接扔进去等更新
        }
        else
        {
            long long ans=sum(R[x])-sum(L[x]-1);//查询所有小块的值,多余的进行下面的更新
             for(auto X:vec)
                ans+=(upper_bound(pos[X].begin(), pos[X].end(),R[x])-lower_bound(pos[X].begin(),pos[X].end(),L[x]))*s[X];
                //vec的元素不会太多,顶多1e5,直接搞进去更新
            printf("%lld\n",ans);
        }
    }
}

 

4765: 普通计算姬

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 1481  Solved: 318
[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
BZOJ这个题目也是啊,但是套路不太一样
 
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
#define N 100005
typedef unsigned long long ll;
int n,m,q,lim,root,belong[N];
int L[N],R[N],tot,cnt[N];
int g[N][320];
ll c[N+N],sum[N],a[N],tag[N];
vector<int>G[N];
void add(int x,int d)
{
    for(; x<=n; x+=x&-x)c[x]+=d;
}
ll Sum(int x)
{
    ll ans=0;
    for(; x>0; x-=x&-x)ans+=c[x];
    return ans;
}
void dfs(int x,int fa)
{
    L[x]=++tot,add(L[x],a[x]),++cnt[belong[x]],sum[x]=a[x];
    for(int i=1; i<=lim; i++)g[x][i]=cnt[i];
    int l=G[x].size();
    for(int i=0,X;i<l;i++)
    {
        X=G[x][i];
        if(X==fa)continue;
        dfs(X,x);
        sum[x]+=sum[X];
    }
    R[x]=tot,--cnt[belong[x]],tag[belong[x]]+=sum[x];
}
ll query(int l,int r)
{
    ll ans=0;
    for(int i=l;i<=min(r,belong[l]*m);i++)ans+=Sum(R[i])-Sum(L[i]-1);
    if(belong[l]!=belong[r])
    {
        for(int i=(belong[r]-1)*m+1;i<=r;i++)ans+=Sum(R[i])-Sum(L[i]-1);
    }
    for(int i=belong[l]+1;i<=belong[r]-1;i++)ans+=tag[i];
    return ans;
}
int main()
{
    cin>>n>>q;
    m=sqrt(n+0.5);//一块有几个
    for(int i=1; i<=n; i++)cin>>a[i],belong[i]=(i-1)/m+1;
    lim=belong[n];
    for(int i=1,u,v; i<=n; ++i)
    {
        cin>>u>>v;
        if(!u)root=v;
        else G[u].push_back(v),G[v].push_back(u);
    }
    dfs(root,-1);
    for(int i=0,op,u,v; i<q; i++)
    {
        cin>>op>>u>>v;
        if(op==1)
        {
            for(int i=1; i<=lim; i++)tag[i]+=g[u][i]*1LL*(v-a[u]);
            add(L[u],v-a[u]),a[u]=v;
        }
        else cout<<query(u,v)<<"\n";
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/BobHuang/p/9640218.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值