CDOJ1590-dfs+树链剖分(2017 UESTC Training for Data Structures O)

传送门:CDOJ1590



题目大意:


给你一颗n个节点的树,根为T,初始时所有节点的值为0,然后给你m次操作,三种操作

1,更新一个子树,节点a的子树节点都加上b

2,更新一条树链,将从u-v的所有节点都加上c

3,查询节点的值


题目思路:


这题更新子树很容易想到dfs序,更新树链很容易想到树链剖分,但是如果我们理解树链剖分

的话就知道树链剖分建立重链的过程就是按照dfs序的过程,所以我们在连接重链哪里用个数组

记录子树结束的时间戳,所以在更新子树的时候就按照dfs的方法更新区间[in[a],out[a]]],树链就是

树链剖分的方法,


AC代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e5+100;

int n,m,T;
int val[maxn];
//邻接表
struct st
{
    int v,nex;
}edge[maxn<<2];

int hed[maxn],e;

void add(int u,int v)
{
    edge[e].v = v,edge[e].nex = hed[u],hed[u] = e++;
}

//树链剖分

int Deep[maxn],Size[maxn],fa[maxn],Son[maxn],top[maxn],in[maxn],out[maxn],rk[maxn];
int tid;

void dfs1(int u,int f,int d)
{
    Deep[u] = d;
    fa[u] = f;
    Size[u] = 1;
    for(int i=hed[u];~i;i=edge[i].nex)
    {
        int v = edge[i].v;
        if(v!=f)
        {
            dfs1(v,u,d+1);
            Size[u]+=Size[v];
            if(Son[u]==-1||Size[v]>Size[Son[u]])
            {
                Son[u] = v;
            }
        }
    }
}


void dfs2(int u,int t)
{
    top[u] = t;
    in[u] = ++tid;  //开始的时间戳
    rk[tid] = u;
    if(Son[u]!=-1)
    {
        dfs2(Son[u],t);
        for(int i=hed[u];~i;i=edge[i].nex)
        {
            int v = edge[i].v;
            if(v!=Son[u]&&v!=fa[u])
                dfs2(v,v);
        }

    }
    out[u] = tid;   //结束的时间戳
}

//线段树
int Tree[maxn<<2],c[maxn<<2];

void Build(int l,int r,int rt)
{
    c[rt] = 0;
    if(l==r)
    {
        Tree[rt] = 0;
        return ;
    }
    int mid = (l+r)>>1;
    Build(l,mid,rt<<1);
    Build(mid+1,r,rt<<1|1);
    Tree[rt] = Tree[rt<<1]=Tree[rt<<1|1];
}

void pushdown(int rt,int m)
{
    if(c[rt])
    {
        c[rt<<1]+=c[rt];
        c[rt<<1|1]+=c[rt];
        Tree[rt<<1]+=(m-m/2)*c[rt];
        Tree[rt<<1|1]+=(m/2)*c[rt];
        c[rt] = 0;
    }

}

void updata(int L,int R,int l,int r,int rt,int v)
{
    if(L<=l&&r<=R)
    {
        c[rt]+=v;
        Tree[rt]+=(r-l+1)*v;

        return ;
    }
    pushdown(rt,r-l+1);
    int mid = (l+r)>>1;
    if(L<=mid)updata(L,R,l,mid,rt<<1,v);
    if(R>mid)updata(L,R,mid+1,r,rt<<1|1,v);
    Tree[rt] = Tree[rt<<1]+Tree[rt<<1|1];
}

int quary(int l,int r,int rt,int pos)
{
   if(l==r)
   {
       return Tree[rt];
   }
   pushdown(rt,r-l+1);
   int mid = (l+r)>>1;
   if(pos<=mid)return quary(l,mid,rt<<1,pos);
   else return quary(mid+1,r,rt<<1|1,pos);
}



//运行


void change(int x,int y,int v)
{
    while(top[x]!=top[y])
    {
        if(Deep[top[x]]<Deep[top[y]])swap(x,y);
        updata(in[top[x]],in[x],1,n,1,v);
        x = fa[top[x]];
    }
    if(Deep[x]>Deep[y])swap(x,y);
    updata(in[x],in[y],1,n,1,v);

}

void init()
{
    tid = e = 0;
    memset(hed,-1,sizeof(hed));
    memset(Son,-1,sizeof(Son));
}

int main()
{
    init();
    cin>>n>>m>>T;
    for(int i=1;i<n;i++)
    {
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(T,T,1);
    dfs2(T,T);
    Build(1,n,1);
    while(m--)
    {
        int k;scanf("%d",&k);
        int a,b,c;
        if(k==1)
        {
            scanf("%d%d",&a,&b);
            updata(in[a],out[a],1,n,1,b);
        }
        else if(k==2)
        {
            scanf("%d%d%d",&a,&b,&c);
            change(a,b,c);
        }
        else
        {
            scanf("%d",&a);
            printf("%d\n",quary(1,n,1,in[a]));
        }
    }
    return 0;
}










全部评论

相关推荐

上周组里招人,我面了六个候选人,回来跟同事吃饭的时候聊起一个让我挺感慨的现象。前三个候选人,算法题写得都不错。第一道二分查找,五分钟之内给出解法,边界条件也处理得干净。第二道动态规划,状态转移方程写对了,空间复杂度也优化了一版。我翻他们的简历,力扣刷题量都在300以上。后三个呢,就有点参差不齐了。有的边界条件没处理好,有的直接说这道题没刷过能不能换个思路讲讲。其中有一个女生,我印象特别深——她拿到题之后没有马上写,而是先问我:“面试官,我能先跟你确认一下我对题目的理解吗?”然后她把自己的思路讲了一遍,虽然最后代码写得不是最优解,但整个沟通过程非常顺畅。这个女生的代码不是最优的,但当我问她“如果这里是线上环境,你会怎么设计’的时候,她给我讲了一套完整的方案——异常怎么处理、日志怎么打、怎么平滑发布。她对这是之前在实习的时候踩过的坑。”我在想LeetCode到底在筛选什么?我自己的经历可能有点代表性。我当年校招的时候,也是刷了三百多道题才敢去面试。那时候大家都刷,你不刷就过不了笔试关。后来工作了,前三年基本没再打开过力扣。真正干活的时候,没人让你写反转链表,也没人让你手撕红黑树。更多的是:这个接口为什么慢了、那个服务为什么OOM了、线上数据对不上了得排查一下。所以后来我当面试官,慢慢调整了自己的评判标准。算法题我还会出,但目的变了。我出算法题,不是想看你能不能背出最优解。而是想看你拿到一个陌生问题的时候,是怎么思考的。你会先理清题意吗?你会主动问边界条件吗?你想不出来的时候会怎么办?你写出来的代码,变量命名乱不乱、结构清不清楚?这些才是工作中真正用得到的能力。LeetCode是一个工具,不是目的。它帮你熟悉数据结构和常见算法思路,这没问题。但如果你刷了三百道题,却说不清楚自己的项目解决了什么问题、遇到了什么困难、你是怎么解决的,那这三百道题可能真的白刷了。所以还要不要刷LeetCode?要刷,但别只刷题。刷题的时候,多问自己几个为什么:为什么用这个数据结构?为什么这个解法比那个好?如果换个条件,解法还成立吗?把刷题当成锻炼思维的方式,而不是背答案的任务。毕竟面试官想看到的,从来不是一台背题机器,而是一个能解决问题的人。
牛客51274894...:意思是光刷力扣还不够卷
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务