ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang

题目链接:https://nanti.jisuanke.com/t/31451

题意:给出一棵有n个点的树,每个点的初始值都是0,m次查询,如果op==1,那么后面接两个数字,表示把第a层的点的值增加b,如果op==2,那么后面接一个数a,表示查询以点a为根节点的这颗子树的所有点的值的和,输出这个值。

思路:因为n,m都可能达到1e5,因为要查找一棵树所有点的和,并且还要修改,如果用树结构查找肯定不方便,那么我们可以用DFS序把树转化为线性结构,用每个点的DSF序来表示这个点,那么查找的时候一棵子树就是一个连续区间,那么我们就可以用树状数组来查找一段区间的和,如果只用树状数组的单点更新,那么极端情况下某一层的点数非常多,更新就会超时;为了解决某一层点非常多并且要更新的情况,我们就可以使用分块的思想来减少更新所需的时间。把所有的层分成大块(点非常多)和小块(点比较少),如果是小块,我们就用树状数组来处理它,更新时暴力更新这一层所有点。如果是大块,我们就用标记数组来记录该层增加的值,因为我们用树的DFS序来代表点,并且DFS序是升序的的,我们就可以用二分查找每个大块来计算目标子树的和。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 100005
/*struct point{
    int u,w;
};
bool operator <(const point &s1,const point &s2)
{
    if(s1.w!=s2.w)
    return s1.w>s2.w;
    else
    return s1.u>s2.u;
}*/
struct node{
    int v,next;
}edge[maxn*2];
int head[maxn],L[maxn],R[maxn],pre[maxn];//L[i]和R[i]数组表示i点DFS序的左右边界,pre[i]数组记录i的父亲,这里用来判断有没有走过
LL c[maxn],up[maxn];//c数组就是树状数组,up数组用来记录点数很多的层数(大块)的增加值
int n,m,k,r,cnt,Time,max_deep,block;//Time就是DFS序,max_deep记录最大的层数,block用来分块
vector<int>ve[maxn];//ve[deep]记录在第deep层的点
vector<int>vv; //vv存大块所在的层
void init()
{
    fill(head,head+maxn-1,-1);
    fill(pre,pre+maxn-1,0);
    fill(c,c+maxn-1,0);
    fill(up,up+maxn-1,0);
    for(int i=0;i<=n;i++)
    ve[i].clear();
    vv.clear();
    Time=cnt=max_deep=0;
    block=sqrt(n);
}
void DFS(int u,int deep)
{
    L[u]=++Time;//记录左边界
    max_deep=max(max_deep,deep);
    ve[deep].push_back(Time);//把u点的DFS序值存入第deep层,我们用DFS序把把树转化为线性,然后用树状数组求区间和
    for(int i=head[u];i!=-1;i=edge[i].next)
    {

        int v=edge[i].v;
        if(pre[v])
        continue;
        pre[v]=u;
        DFS(v,deep+1);
    }
    R[u]=Time;//存右边界,注意Time不要加,因为一个点不用记录多个DFS序
}
void add(int u,int v)
{
    edge[++cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}
int lowbit(int x)
{
    return x&(-x);
}
void Add(int x,int b)//树状数组单点更新
{
    for(int i=x;i<=n;i+=lowbit(i))
    c[i]+=b;
}
LL sum(int x)//树状数组求区间和
{
    if(x<=0)
    return 0;
    LL ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        pre[1]=-1;
        DFS(1,0);//u,depth
        for(int i=0;i<=max_deep;i++)//把大块存进vv数组
        {
            if(ve[i].size()>block)
            vv.push_back(i);
        }
        int op;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&op);
            if(op==1)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                if(ve[a].size()>block)//如果a层是大块
                {
                    up[a]+=b;
                }
                else//a层不是大块
                {
                    for(int i=0;i<ve[a].size();i++)//暴力更新每个点
                    Add(ve[a][i],b);
                }
            }
            else
            {
                int a;
                scanf("%d",&a);
                int l=L[a],r=R[a];
                LL ans=sum(r)-sum(l-1);//我们在树状数组里存的是DFS序,求和是根据DFS序来求和
                for(int i=0;i<vv.size();i++)//遍历所有大块来寻找属于子树的值
                {
                    int idx=vv[i];
                    ans+=(upper_bound(ve[idx].begin(),ve[idx].end(),r)-lower_bound(ve[idx].begin(),ve[idx].end(),l))*up[idx];
                }
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/6262369sss/p/9732675.html

时间: 2024-08-06 12:34:52

ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang的相关文章

ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang(树上分块+dfs序+线段树)

题意 链接:https://nanti.jisuanke.com/t/A1998 给出一个有根树(根是1),有n个结点.初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L(根节点深度为0)的点的值全部增加X.操作2.查询以x为根的子树的结点值得和.其中N,Q<=1e5. 思路 因为这题是对某一深度的所有点加x,所以不是树链剖分. 我们可以先预处理一下dfs序,顺带把d[u]:u的深度.dd[x]:深度为x的点集求出来. 考虑分块,对某一深度分两种情况:1.这一深度的点的

【ACM-ICPC 2018 沈阳赛区网络预赛 I】Lattice&#39;s basics in digital electronics

[链接] 我是链接,点我呀:) [题意] 每个单词的前缀都不同. 不能更明示了... 裸的字典树. 模拟一下.输出一下就ojbk了. [题解] #include <bits/stdc++.h> #define LL long long #define rep1(i,a,b) for (int i = a;i <= b;i++) #define rep2(i,a,b) for (int i = a;i >= b;i--) #define all(x) x.begin(),x.end(

ACM-ICPC 2018 沈阳赛区网络预赛 F. Fantastic Graph

"Oh, There is a bipartite graph.""Make it Fantastic." X wants to check whether a bipartite graph is a fantastic graph. He has two fantastic numbers, and he wants to let all the degrees to between the two boundaries. You can pick up sev

ACM-ICPC 2018 沈阳赛区网络预赛 K. Supreme Number

A prime number (or a prime) is a natural number greater than 11 that cannot be formed by multiplying two smaller natural numbers. Now lets define a number NN as the supreme number if and only if each number made up of an non-empty subsequence of all

ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer (最大生成树+LCA求节点距离)

ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer J. Maze Designer After the long vacation, the maze designer master has to do his job. A tour company gives him a map which is a rectangle. The map consists of N \times MN×M little squares. That is to say, the h

ACM-ICPC 2018 南京赛区网络预赛 J.Sum

Sum A square-free integer is an integer which is indivisible by any square number except 11. For example, 6 = 2 \cdot 36=2⋅3 is square-free, but 12 = 2^2 \cdot 312=22⋅3 is not, because 2^222 is a square number. Some integers could be decomposed into

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

A. Gudako and Ritsuka 留坑 B. Call of Accepted 留坑 C. Convex Hull 留坑 D. Made In Heaven 留坑 E. The cake is a lie 留坑 F. Fantastic Graph 留坑 G. Spare Tire 留坑 H. Hamming Weight 留坑 I. Lattice's basics in digital electronics 留坑 J. Ka Chang 留坑 K. Supreme Number

线性素数筛 ACM-ICPC 2018 南京赛区网络预赛 J Sum

https://www.jisuanke.com/contest/1555?view=challenges 题意: 题解:写完都没发现是个积性函数233 想法就是对x分解质因数,f(x)就是2^k,其中k是x分解结果中次数为一的质因子个数.如果有某个次数大于等于3,f(x)==0; 这样明显会TLE 所以就想一个递推的方法. 于是魔改了一下线性筛. #include<iostream> #include<cstdlib> #include<cstdio> #includ

ACM-ICPC 2018 沈阳赛区网络预赛 G. Spare Tire

这题很好啊,好在我没做出来...大概分析了一下,题目大概意思就是求 问所有满足1<=i<=n且i与m互素的ai之和 最开始我们队的做法是类似线性筛的方法去筛所有数,把数筛出来后剩下数即可,但是这样的是时间复杂度十分大,我们需要遍历每个质因 的倍数,这样最坏的复杂度是很大的1e8,因为我们需要把i的倍数筛到1e8,这样肯定不行,那么想想其他办法 我们想到了容斥-----(赛后想到的) 我们可以推处一个公式ai=i*i+i; 那么ai的前n项和Tn=n*(n+1)*(2*n+1)/6+n*(n+1