• 【题解】POJ 3417 Network(倍增求LCA+DP+树上差分)


    POJ3417:http://poj.org/problem?id=3417

    思路

    我们注意到由“主要边”构成一颗树 “附加边”则是非树边 把一条附加边(x,y)加入树中 会与树上x,y之间构成一个环

    因此 我们称每条附加边(x,y)都把树上x,y之间的路径覆盖一次 我们只需要统计出每条“主要边”被覆盖几次

    有以下几种情况

    • 第一步把覆盖0次的主要边切断 则第二步可以任意切一条附加边 ans+=m
    • 第一步把覆盖1次的主要边切断 则第二步只有一种选择切附加边 ans+=1
    • 第一步把覆盖2次或以上的主要边切断 则不可能击败Dark

    这样我们就可以统计出ans

    解决每条边的覆盖次数 需要用到树上差分算法 

    我们给每个节点初值为0

    对于每条附加边(x,y) 我们给x和y节点权值+1

    对于x和y的lca节点权值-2(想想为什么)

    设F(x)为以x为根的子树中各节点的权值和 则F(x)就是x与其父节点之间树边被覆盖的次数

    最后统计ans即可

    PS:这题需要把cin cout 改为scanf printf 不然会TLE 坑了我一晚上

     代码

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define maxn 100010
    int n,m,cnt,ans;
    int h[maxn],dep[maxn],f[maxn][31],vis[maxn];
    struct Edge
    {
        int next;
        int to;
    }e[maxn<<1];
    void add(int u,int v)
    {
        e[++cnt].to=v;
        e[cnt].next=h[u];
        h[u]=cnt;
    }
    void deal(int u,int fa)
    {
        dep[u]=dep[fa]+1;//深度等于父节点+1 
        for(int i=1;i<=20;i++)//枚举倍增步数 
        {
            f[u][i]=f[f[u][i-1]][i-1];//二分思想 
        }
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==fa) continue;//如果是父节点跳过 
            f[v][0]=u;//如果是子节点 
            deal(v,u);
        }
    }
    int lca(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);//保证x的深度大 
        for(int i=20;i>=0;i--)//从大开始枚举 
        {
            if(dep[f[x][i]]>=dep[y]) x=f[x][i];//先跳到同一层 
            if(x==y) return x;//如果已经找到 
        }
        for(int i=20;i>=0;i--)//此时x y已经在同一层 
        {
            if(f[x][i]!=f[y][i])//如果不同就继续跳 
            {
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }
    void dp(int x)
    {
        for(int i=h[x];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==f[x][0]) continue;
            dp(v);//不是父亲时 继续找 
            vis[x]+=vis[v];//递归回来时加上所有节点的权值 
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);//无向图 
            add(b,a);
        }
        deal(1,0);//预处理 
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            vis[a]++;//树上差分算法 
            vis[b]++;
            vis[lca(a,b)]-=2;
        }
        dp(1);//计算所有主要边被覆盖几次 
        for(int i=2;i<=n;i++)//从2开始是因为我们把点存成边 
        {
            if(vis[i]==0)//被覆盖0次 
            ans+=m;
            if(vis[i]==1)//被覆盖1次 
            ans++;
        }
        printf("%d",ans);
    }
    View Code
  • 相关阅读:
    Python实现机器人聊天
    node.js使用express框架进行文件上传
    nginx让所有的http地址重定向到https
    nginx配置https
    vscode源码编译运行打包使其由英文变为中文
    阿里云配置tomcat https
    springboot打成的jar包如何在Linux上持久运行
    wordpress数据表分析
    DevExpress Components16.2.6 Source Code 重编译教程
    DataGridView绑定泛型List时,利用BindingList来实现增删查改
  • 原文地址:https://www.cnblogs.com/BrokenString/p/9769718.html
Copyright © 2020-2023  润新知