• 树代码&题解整活


    1、LCA

    1.1 LCA模板

    运用倍增思想,向根方向寻找,倍增跳跃。

    #include <bits/stdc++.h>
    using namespace std;
    const int  N = 5E5 + 5;
    int f[N][20], s, n, m, x, y, dep[N], b[N];
    vector <int> g[N];
    void dfs(int xx)
    {
        for(int i = 1; i <= 16; i++)
        {
            f[xx][i] = f[f[xx][i - 1]][i - 1]; 
        }
        for(int i = 0; i < g[xx].size(); i++)
        {
            int temp = g[xx][i];
            if(!b[temp])
            {
                b[temp] = 1;
                dep[temp] = dep[xx] + 1;
                f[temp][0] = xx;
                dfs(temp);
            }
        }
    }
    int LCA(int x, int y)
    {
        if(dep[x] < dep[y])
        {
            swap(x, y);
        }
        int temp = dep[x] - dep[y];
        for(int i = 16; i >= 0; i--)
        {
            if((1 << i) & temp)
            {
                x = f[x][i];
            }
        }
        if(x == y)
        {
            return x;
        }
        for(int i = 16; i >= 0; i--)
        {
            if(f[x][i] != f[y][i])
            {
                x = f[x][i];
                y = f[y][i];
            }
        }
        return f[x][0];
    }
    int main()
    {
        cin >> n >> m >> s;
        for(int i = 1; i < n; i++)
        {
            cin >> x >> y;
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dep[s] = 1;
        b[s] = 1;
        dfs(s);
        while(m--)
        {
            cin >> x >> y;
            cout << LCA(x, y) << endl;
        }
    }
    View Code

    2、树上前缀和-博客

    寻找树上任意两点的距离,做法之一为树上前缀和

    将两个点到根的距离之和相加即为答案。

    2.1 模板

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N = 200005;
    const int M = 25;
    int Q, n, m, head[N], dou[N][M], cnt, dep[N], b[N], k, total[N], wei[N];
    struct node {
        int u, v, next, sum;
    } ed[N];
    void add_edge(int u, int v, int k) {
        cnt++;
        ed[cnt].u = u;
        ed[cnt].v = v;
        ed[cnt].next = head[u];
        ed[cnt].sum = k;
        head[u] = cnt;
    }
    void dfs(int xx) {
        for (int i = 1; i <= 20; i++) {
            dou[xx][i] = dou[dou[xx][i - 1]][i - 1];
        }
    
        for (int i = head[xx]; i != 0; i = ed[i].next) {
            int temp = ed[i].v;
    
            if (b[temp] == 0) {
                b[temp] = 1;
                dep[temp] = dep[xx] + 1;
                total[temp] = ed[i].sum + total[xx];
                dou[temp][0] = xx;
                dfs(temp);
            }
        }
    }
    int LCA(int x, int y) {
        if (dep[x] < dep[y])
            swap(x, y);
    
        int temp = dep[x] - dep[y];
    
        for (int i = 20; i >= 0; i--) {
            if (1 << i & temp) {
                x = dou[x][i];
            }
        }
    
        if (x == y)
            return x;
    
        for (int i = 20; i >= 0; i--) {
            if (dou[x][i] != dou[y][i]) {
                x = dou[x][i];
                y = dou[y][i];
            }
        }
    
        return dou[x][0];
    }
    int main() {
        cin >> n;
        cin >> m;
    
        for (int i = 1; i < n; i++) {
            int x, y, k;
            scanf("%d%d%d", &x, &y, &k);
            add_edge(x, y, k);
            add_edge(y, x, k);
        }
    
        b[1] = 1;
        dep[1] = 1;
        dfs(1);
    
        while (m--) {
            int x, y;
            scanf("%d%d", &x, &y);
            int lca = LCA(x, y);
            cout << total[x] + total[y] - total[lca] - total[lca] << endl;
        }
    
        return 0;
    }
    View Code

    2.2 JLOI2009]二叉树问题

    上行边数$*2+$ 下行边数$*1$,可以把整个树的每一条边看成权值均为$1$ 的树,跑前缀和,左边到根的距离$*2+$ 右边到根的距离$*1$。

    在求前缀和的过程中顺便把树的深度、宽度一把搞掉。

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    vector <int> mapp[105];
    int n,anslen[105],len[105],maxlen,dep[105],b[105],maxdep,maxnlong=1,dou[10005][25]; 
    void dfs(int x)
    {
        for(int i=1;i<=15;i++)
        {
            dou[x][i]=dou[dou[x][i-1]][i-1];
        }
        for(int i=0;i<mapp[x].size();i++)
        {
            if(!b[mapp[x][i]])
            {
                int temp=mapp[x][i]; 
                dep[temp]=1+dep[x];
                dou[temp][0]=x;
                b[mapp[x][i]]=1; 
                len[temp]=dep[temp];
                maxdep=max(dep[temp],maxdep);
                dfs(temp);
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])
        swap(x,y);
        int temp=dep[x]-dep[y];
        for(int i=16;i>=0;i--)
        {
            if(1<<i&temp)
            {
                x=dou[x][i];
            }
        }
        if(x==y)
        return x;
        for(int i=16;i>=0;i--)
        {
            if(dou[x][i]!=dou[y][i])
            {
                x=dou[x][i];
                y=dou[y][i];
            }
        }
        return dou[x][0];
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            cin>>x>>y;
            mapp[x].push_back(y);
            mapp[y].push_back(x);
        }
        b[1]=1;
        dep[1]=1;
        maxdep=1;
        len[1]=1;
        dfs(1);
        int u,v;
        cin>>u>>v;
        int lca=LCA(u,v);
        cout<<maxdep<<endl;
        for(int i=1;i<=n;i++)
        {
            anslen[len[i]]+=1;
        }
        for(int i=1;i<=maxdep;i++)
        {
            maxlen=max(maxlen,anslen[i]);
        }
        cout<<maxlen<<endl;
        cout<<(dep[u]-dep[lca])*2+(dep[v]-dep[lca]);
        return 0;
    }
    View Code

    2.3 [AHOI2008]紧急集合 / 聚会


    给定三个点,找到一个点,使得该点到题目所给的三个点的距离最小。

    可以使用树上前缀和来写,需要一些常数上的优化,当然注意卡常

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=1000005;
    const int M=25; 
    int dep[N],cnt,dou[N][M],n,m,x,y,z,b[N];
    int head[N];
    struct node{
        int u,v,next;
    }ed[N];
    inline int read()
    {
        int f=1;
        int xx=0;
        char c=getchar();
        while(!isdigit(c))
        {
            if(c=='-')
            f*=-1;
            c=getchar();
        }
        while(isdigit(c))
        {
            xx=xx*10+c-'0';
            c=getchar();
        }
        return xx*f;
    }
    void add_edge(int u,int v)
    {
        ed[++cnt].u=u;
        ed[cnt].v=v;
        ed[cnt].next=head[u];
        head[u]=cnt;    
    }
    void dfs(int xx)
    {
        for(int i=1;i<=16;i++)
        {
            dou[xx][i]=dou[dou[xx][i-1]][i-1];
        }
        for(int i=head[xx];i!=0;i=ed[i].next)
        {
            if(b[ed[i].v]==0)
            {
                int temp=ed[i].v;
                b[temp]=1;
                dep[temp]=dep[xx]+1;
                dou[temp][0]=xx;
                dfs(temp); 
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])
        swap(x,y);
        int temp=dep[x]-dep[y];
        for(int i=16;i>=0;i--)
        {
            if(1<<i&temp)
            x=dou[x][i];
        } 
        if(x==y)
        return y;
        for(int i=16;i>=0;i--)
        {
            if(dou[x][i]!=dou[y][i])
            {
                x=dou[x][i];
                y=dou[y][i];
            }
        }
        return dou[x][0];
    }
    int main()
    {
        n=read();
        m=read();
        for(int i=1;i<n;i++)
        {
            x=read();
            y=read();
            add_edge(x,y);
            add_edge(y,x); 
        }
        dep[1]=1;
        b[1]=1;
        dfs(1);
        while(m--)
        {
            x=read();
            y=read();
            z=read();
            int xy=LCA(x,y);
            int yz=LCA(y,z);
            int xz=LCA(x,z);
            int ans=0,ansmoney=0;
            if(xy==yz)
            ans=xz;
            else if(xy==xz)
            ans=yz;
            else if(yz==xz)
            ans=xy;
            cout<<ans<<" ";
            ansmoney+=dep[x]+dep[y]-dep[xy]*2;
            ansmoney+=dep[y]+dep[z]-dep[yz]*2;
            ansmoney+=dep[x]+dep[z]-dep[xz]*2;
            ansmoney/=2;
            cout<<ansmoney<<endl;
        }
        return 0;
    }
    View Code

     3、树上差分

    为前缀和的逆运算,所以与前缀和做法类同。

    3.1 模板

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=100005;
    const int M=25;
    int cnt,head[N],dou[N][M],total[N],n,m,dep[N],b[N];
    struct node{
        int u,v,w,next;
    }ed[N];
    void add_edge(int u,int v)
    {
        cnt++;
        ed[cnt].u=u;
        ed[cnt].v=v;
        ed[cnt].next=head[u];
    
        head[u]=cnt;
    }
    void dfs(int xx)
    {
        for(int i=1;i<=20;i++)
        {
            dou[xx][i]=dou[dou[xx][i-1]][i-1];
        }
        for(int i=head[xx];i;i=ed[i].next)
        {
            int temp=ed[i].v;
            if(!b[temp])
            {
                b[temp]=1;
                dep[temp]=dep[xx]+1;
                dou[temp][0]=xx;
                dfs(temp);
            }
        }
    } 
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])
        {
            swap(x,y);
        }
        int temp=dep[x]-dep[y];
        for(int i=20;i>=0;i--)
        {
            if((1<<i)&temp)
            {
                x=dou[x][i];
            }
        }
        if(x==y)
        return x;
        for(int i=20;i>=0;i--)
        {
            if(dou[x][i]!=dou[y][i])
            {
                x=dou[x][i];
                y=dou[y][i];
            }
        }
        return dou[x][0];
    }
    void dfs2(int xx)
    {
        for(int i=head[xx];i;i=ed[i].next)
        {
            int temp=ed[i].v;
            if(!b[temp])
            {
                b[temp]=1;
                dfs2(temp);
                total[xx]+=total[temp];
            }
        }
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            add_edge(x,y);
            add_edge(y,x);
        }
        b[1]=1;
        dep[1]=1;
        dfs(1);
        memset(b,0,sizeof b);
        while(m--)
        {
            int xx,yy;
            cin>>xx>>yy;
            int lca=LCA(xx,yy);    
            total[xx]++;
            total[yy]++;
            total[lca]--;
            total[dou[lca][0]]--;
        }
        b[1]=1;
        dfs2(1);
        int maxn=0;
        for(int i=1;i<=n;i++)
        {
            maxn=max(maxn,total[i]);
        }
        cout<<maxn;
        return 0;
    }
    View Code

    3.2 松鼠的新家


    做法与差分模板类似。

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N=1000005;
    const int M=25;
    int cnt,n,head[N],fa[N][M],dep[N],b[N],total[N],a[N],bt[N];
    struct node{
        int u,v,next;
    }ed[N]; 
    void add_edge(int u,int v)
    {
        cnt++;
        ed[cnt].u=u;
        ed[cnt].v=v;
        ed[cnt].next=head[u];
        head[u]=cnt;
    }
    void dfs(int xx)
    {
        for(int i=1;i<=20;i++)
        {
            fa[xx][i]=fa[fa[xx][i-1]][i-1];
        }
        for(int i=head[xx];i;i=ed[i].next)
        {
            int temp=ed[i].v;
            if(!b[temp])
            {
                b[temp]=1;
                dep[temp]=dep[xx]+1;
                fa[temp][0]=xx;
                dfs(temp);
            }
        }
    }
    void dfs2(int xx)
    {
        for(int i=head[xx];i;i=ed[i].next)
        {
            int temp=ed[i].v;
            if(!b[temp])
            {
                b[temp]=1;
                dfs2(temp);
                total[xx]+=total[temp];
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])
        {
            swap(x,y);
        }
        int temp=dep[x]-dep[y];
        for(int i=20;i>=0;i--)
        {
            if(1<<i&temp)
            {
                x=fa[x][i];
            }
        }
        if(x==y)
        {
            return x;
        }
        for(int i=20;i>=0;i--)
        {
            if(fa[x][i]!=fa[y][i])
            {
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        cin>>a[i];
        for(int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            add_edge(x,y);
            add_edge(y,x); 
        } 
        b[1]=1;
        dep[1]=1;
        dfs(1);
        memset(b,0,sizeof b);
        for(int i=2;i<n;i++)
        {
            int x=a[i-1];
            int y=a[i];
            if(!b[x])
            {
                total[x]++;
                b[x]=1;
            }
            else
            {
                total[fa[x][0]]++;
            }
            if(!b[y])
            {
                total[y]++;
                b[y]=1; 
            } 
            else
            {
                total[fa[y][0]]++;
            }
            int lca=LCA(x,y);
            total[lca]--;
            total[fa[lca][0]]--; 
        }
        int x=a[n-1];
        int y=a[n];
        total[fa[x][0]]++;
        total[fa[y][0]]++;
        int lca=LCA(x,y);
        total[lca]--;
        total[fa[lca][0]]--; 
        memset(b,0,sizeof b);
        b[1]=1;
        dfs2(1);
        for(int i=1;i<=n;i++)
        {
            cout<<total[i]<<endl;
        }
        return 0;
    }
    View Code

    4、数的直径

    一些无向边且权值均为正整数构成的树上(可以无根),距离最长的2个点的距离为树的直径。

  • 相关阅读:
    dd命令测试IO
    手工释放linux内存——/proc/sys/vm/drop_caches
    CNN中的卷积理解和实例
    Python map() 函数
    Python filter() 函数
    Python 用 os.walk 遍历目录
    在python中读写matlab文件
    人生, 不要在别扭的事上纠结
    javascript 最佳实践 ( 24 章 )
    CSS权威指南(第3版)
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/14603536.html
Copyright © 2020-2023  润新知