1 条题解

  • 0
    @ 2025-8-24 21:53:13

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar chen_zhe
    Aya 敲可爱的~

    搬运于2025-08-24 21:53:13,当前版本为作者最后更新于2017-11-10 16:01:18,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    前置题目:P3384

    这道题目是一个模板题(省选考模板?)

    作为一个 STL 的忠实拥护者,怎么能只有数组写链式前向星的题解呢?

    这道题的数据还是比较水的,因此有两种做法。

    第一种做法:倍增跳 LCA,然后用树状数组或者线段树维护一下子树和链就可以了

    时间复杂度 O(nlogn)O(n \log n),常数较大

    (其实数据出好一点这个就真的不行了,不信用这种方法做 P3384)

    第二种做法:树链剖分

    在这里顺便说一下树链剖分:

    树链剖分是把一个树分成若干条链,最常用的是轻重链剖分。一个节点 uu 的重链所连接的节点 vv,有一个特征,就是 vv 的子树是最大的,其他的边全都是轻链。通过一些处理(两次dfs)可以求出一个节点的父亲、深度和在线段树上的 idid,那么就可以很方便地做这道题目了。

    时间复杂度 O(nlog2n)O(n \log^2 n) 常数较小,如果把线段树改成树状数组就最好了

    我这里的代码对于输入的数据做了处理,即每个读入的节点的编号加 11,当然不这样处理也可以,把第一次处理时的 fafa 变成一个很大的数,比如 n+100n+100 之类的。不过一般的题目数据不会卡的。

    然后再介绍一个小技巧

    char c;
    scanf("%s",&c);
    

    可以有效地去除空格

    好了代码如下:

    #include <iostream>
    #include <cstdio>
    #include <vector>
    
    using namespace std;
    
    int n,m,r,father[100050],depth[100050],size[100050],son[100050],top[100050],ide[100050],rid[100050],cnt=0;
    
    long long val[100050],p;
    
    vector <int> graph[100050];
    
    struct SegTree
    {
        int left,right;
        long long value,tag;
    }t[500050];
    
    void dfs1(int u,int fa)
    {
        father[u]=fa;
        depth[u]=depth[fa]+1;
        size[u]=1;
        for (int i=0;i<graph[u].size();i++)
        {
            int v=graph[u][i];
            if (v!=fa)
            {
                dfs1(v,u);
                size[u]+=size[v];
                if (size[v]>size[son[u]])
                    son[u]=v;
            }
        }
    }
    
    void dfs2(int now,int fir)
    {
        cnt++;
        top[now]=fir;
        ide[now]=cnt;
        rid[ide[now]]=now;
        if (!son[now]) 
            return;
        dfs2(son[now],fir);
        for (int i=0;i<graph[now].size();i++)
        {
            int v=graph[now][i];
            if (v!=father[now] && v!=son[now])
                dfs2(v,v);
        }
    }
    
    void Push_Up(int id)
    {
        t[id].value=t[id<<1].value+t[id<<1|1].value;
    }
    
    void Push_Down(int id)
    {
        t[id<<1].tag+=t[id].tag;
        t[id<<1|1].tag+=t[id].tag;
        t[id<<1].value+=t[id].tag*(t[id<<1].right-t[id<<1].left+1);
        t[id<<1|1].value+=t[id].tag*(t[id<<1|1].right-t[id<<1|1].left+1);
        t[id].tag=0;
    }
    
    void Build(int id,int left,int right)
    {
        t[id].left=left;
        t[id].right=right;
        if (left==right)
        {
            t[id].value=val[rid[left]];
            return;
        }
        int mid=(left+right)>>1;
        Build(id<<1,left,mid);
        Build(id<<1|1,mid+1,right);
        Push_Up(id);
    }
    
    void Update(int id,int left,int right,long long value)
    {
        if (t[id].left>=left && t[id].right<=right)
        {
            t[id].tag+=value;
            t[id].value+=value*(t[id].right-t[id].left+1);
            return;
        }
        Push_Down(id);
        int mid=(t[id].left+t[id].right)>>1;
        if (right<=mid)
            Update(id<<1,left,right,value);
        else if (left>mid)
            Update(id<<1|1,left,right,value);
        else
        {
            Update(id<<1,left,mid,value);
            Update(id<<1|1,mid+1,right,value); 
        }
        Push_Up(id);
    }
    
    long long Query(int id,int left,int right)
    {
        if (t[id].left>=left && t[id].right<=right)
            return t[id].value;
        else
        {
            int mid=(t[id].left+t[id].right)>>1;
            Push_Down(id);
            if (right<=mid)
                return Query(id<<1,left,right);
            if (left>mid)
                return Query(id<<1|1,left,right);
            else
                return Query(id<<1,left,mid)+Query(id<<1|1,mid+1,right);
        }
    }
    
    void SubTreeChange(int id,int value)
    {
        int x=ide[id];
        int y=ide[id]+size[id]-1;
        Update(1,x,y,value);
    }
    
    long long SubTreeQuery(int id)
    {
        int x=ide[id];
        int y=ide[id]+size[id]-1;
        return Query(1,x,y);
    }
    
    void LinkChange(int x,int y,int value)
    {
        int tx=top[x];
        int ty=top[y];
        while (tx!=ty)
        {
            if (depth[tx]<depth[ty])
            {
                swap(x,y);
                swap(tx,ty);
            }
            Update(1,ide[tx],ide[x],value);
            x=father[tx];
            tx=top[x];
        }
        if (depth[x]>depth[y])
            swap(x,y);
        Update(1,ide[x],ide[y],value);
    }
    
    long long LinkQuery(int x,int y)
    {
        int tx=top[x];
        int ty=top[y];
        long long ans=0;
        while (tx!=ty)
        {
            if (depth[tx]<depth[ty])
            {
                swap(x,y);
                swap(tx,ty);
            }
            ans+=Query(1,ide[tx],ide[x]);
            x=father[tx];
            tx=top[x];
        }
        if (depth[x]>depth[y])
            swap(x,y);
        ans+=Query(1,ide[x],ide[y]);
        return ans;
    }
    
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            graph[u+1].push_back(v+1);
            graph[v+1].push_back(u+1);
        }
        dfs1(1,0);
        dfs2(1,1);
        scanf("%d",&m);
        Build(1,1,cnt);
        for (int i=1;i<=m;i++)
        {
            int u,v,d;
            char c;
            scanf("%s",&c);
            if (c=='A')
            {
                scanf("%d%d%d",&u,&v,&d);
                LinkChange(u+1,v+1,d);
            }
            else
            {
                scanf("%d",&u);
                printf("%lld\n",SubTreeQuery(u+1));
            }
        }
        return 0;
    }
    

    倍增+线段树做法

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <cstdlib>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    
    struct Seg_Tree
    {
        int left,right,add;
        long long value;
    }son[400050];
    
    int n,m;
    
    vector <int> graph[100050];
    
    int fa[100050][16],size[100050],depth[100050];
    
    int Time,pos[100050],belong[100050];
    
    long long delta[500050];
    
    bool vis[100050];
    
    int read()
    {
        int w=0,c=1; 
        char ch=getchar();
        while (ch<'0' || ch>'9')
        {
            if (ch=='-') 
                c=-1;
            ch=getchar();
        }
        while (ch>='0' && ch<='9')
        {
            w=w*10+ch-'0';
            ch=getchar();
        }
        return w*c;
    }
    
    void Build(int id,int left,int right)
    {
        son[id].value=son[id].add=0;
        son[id].left=left;
        son[id].right=right;
        if (left==right)
            return;
        Build(id<<1,left,(left+right)>>1);
        Build(id<<1|1,((left+right)>>1)+1,right);
        son[id].value=son[id<<1].value+son[id<<1|1].value;
    }
    
    void Change_delta(int id,long long value,int left,int right)
    {
        son[id].value+=value*(right-left+1);
        delta[id]+=value;
    }
    
    void Push_Up(int id)
    {
        son[id].value=son[id<<1].value+son[id<<1|1].value;
    }
    
    void Push_Down(int id,int left,int right)
    {
        int mid=(left+right)/2;
        Change_delta(id<<1,delta[id],left,mid);
        Change_delta(id<<1|1,delta[id],mid+1,right);
        delta[id]=0;
    }
    
    void Update(int id,int left,int right,long long value,int pos1,int pos2)
    {
        int mid=(left+right)/2;
        //cout << id << " " << left << " " << right << " " << value << " " << pos1 << " " << pos2 << endl;
        //system("pause");
        if (pos1<=left && pos2>=right)
        {
            Change_delta(id,value,left,right);
            return;
        }
        Push_Down(id,left,right);
        if (pos1<=mid)
            Update(id<<1,left,mid,value,pos1,pos2);
        if (pos2>mid)
            Update(id<<1|1,mid+1,right,value,pos1,pos2);
        Push_Up(id);
    }
    
    void getlca(int u)
    {
        vis[u]=true;
        size[u]=1;
        for (int i=1;i<=16;i++)
        {
            if (depth[u]<(1<<i))
                break;
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        for (int i=0;i<graph[u].size();i++)
        {
            int v=graph[u][i];
            if (vis[v])
                continue;
            depth[v]=depth[u]+1;
            fa[v][0]=u;
            getlca(v);
            size[u]+=size[v];
        }
    }
    
    void dfs(int k,int num)
    {
        int x=0;
        //cout << k << " " << num << endl;
        //system("pause");
        Time++;
        pos[k]=Time;
        belong[k]=num;
        for (int i=0;i<graph[k].size();i++)
        {
            int v=graph[k][i];
            if (depth[v]>depth[k] && size[v]>size[x])
                x=v;
        }
        if (x==0)
            return;
        dfs(x,num);
        for (int i=0;i<graph[k].size();i++)
        {
            int v=graph[k][i];
            if (depth[v]>depth[k] && x!=v)
                dfs(v,v);
        }
    }
    
    void Insert(int x,int y,long long z)
    {
        for (;belong[x]!=belong[y];x=fa[belong[x]][0])
        {
            //cout << x << " " << y << endl;
            if (depth[belong[x]]<depth[belong[y]])
                swap(x,y);
            Update(1,1,n,z,pos[belong[x]],pos[x]);
        }
        if (depth[x]<depth[y])
            swap(x,y);
        //cout << x << " " << y << " " << pos[x] << " " << pos[y] << endl;
        Update(1,1,n,z,pos[y],pos[x]);
    }
    
    long long Query(int id,int left,int right,int pos1,int pos2)
    {
        int mid=(left+right)/2;
        long long ans=0;
        //cout << id << " " << left << " " << right << " " << son[id].value << " " << pos1 << " " << pos2 << endl;
        if (pos1<=left && pos2>=right)
            return son[id].value;
        Push_Down(id,left,right);
        if (pos1<=mid)
            ans+=Query(id<<1,left,mid,pos1,pos2);
        if (pos2>mid)
            ans+=Query(id<<1|1,mid+1,right,pos1,pos2);
        Push_Up(id);
        return ans;
    }
    
    int main()
    {
        //freopen("tree.in","r",stdin);
        //freopen("tree.out","w",stdout);
        scanf("%d",&n);
        for (int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u++;
            v++;
            graph[u].push_back(v);
            graph[v].push_back(u);
        }
        getlca(1);
        dfs(1,1);
        Build(1,1,n);
        m=read();
        //for (int i=1;i<=n;i++)
        //    cout << belong[i] << endl;
        for (int i=1;i<=m;i++)
        {
            char chr;
            int x;
            scanf("%s",&chr);
            scanf("%d",&x);
            x++;
            if (chr=='A')
            {
                int y,z;
                scanf("%d%d",&y,&z);
                y++;
                Insert(x,y,z);
            }
            if (chr=='Q')
                printf("%lld\n",Query(1,1,n,pos[x],pos[x]+size[x]-1));
        }
        return 0;
    }
    
    /*4
    0 1
    1 2
    2 3
    4
    A 1 3 1
    Q 0
    Q 1
    Q 2
    */
    

    实测倍增比树链剖分慢1000ms

    • 1

    信息

    ID
    2799
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者