• POJ3417 Network(树上差分)


    题意:

    Dark 是一张无向图,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 N-1条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 M条附加边。

    你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。

    现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

    题解:

    每条附加边(x,y)都把树上x,y之间路径的每条边覆盖了一次。我们只需要统计每一条主要边被覆盖了多少次。

    如果第一步把被覆盖0次的主要边切断,则第二步可以任意切断一条附加边。若第一步把覆盖一次的主要边切断,则第二步方法唯一。若第一步把被覆盖两次及以上的主要边切断,第二步就无解。

    综上,我们要解决的问题是:给定一张无向图和一颗生成树,求每条树边被非树边覆盖了多少次。

    做法是树上差分。我们给树上每个结点一个初始为0的权值,然后对每条非树边x,y,令x,y的权值加一,LCA(x,y)的权值减二,最后对这棵树进行一次DFS,求出F(x)表示以x为根的子树中个节点的权值之和,F(x)就是x与它父亲结点之间的树边被覆盖的次数。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn=2e5+100;
    int N,M;
    int head[maxn];
    int tol;
    struct node {
        int u;
        int v;
        int w;
        int next;
    }edge[maxn]; 
    void addedge (int u,int v) {
        edge[tol].u=u;
        edge[tol].v=v;
        edge[tol].next=head[u];
        head[u]=tol++;
    }
    int d[maxn];
    int h[maxn];
    int father[20][maxn];
    void dfs (int x) {
        for (int i=head[x];i!=-1;i=edge[i].next) {
            int v=edge[i].v;
            if (v==father[0][x]) continue;
            father[0][v]=x;
            h[v]=h[x]+1;
            d[v]=d[x]+edge[i].w;
            dfs(v);
        }
    }
    int lca (int x,int y) {
        if (h[x]<h[y]) swap(x,y);
        for (int i=17;i>=0;i--) 
            if (h[x]-h[y]>>i) x=father[i][x];
        if (x==y) return x;
        for (int i=17;i>=0;i--) {
            if (father[i][x]!=father[i][y]) {
                x=father[i][x];
                y=father[i][y];
            }
        }
        return father[0][x];
    }
    
    int weight[maxn];
    int f[maxn];
    int visit[maxn];
    void dfs2 (int u) {
        visit[u]=1;
        for (int i=head[u];i!=-1;i=edge[i].next) {
            int v=edge[i].v;
            if (!visit[v]) dfs2(v),f[u]+=f[v];
        }
    }
    int main () {
        while (~scanf("%d%d",&N,&M)) {
            memset(head,-1,sizeof(head));
            tol=0;
            memset(h,0,sizeof(h));
            memset(d,0,sizeof(d));
            memset(father,0,sizeof(father));
            memset(weight,0,sizeof(weight));
            memset(f,0,sizeof(f));
            memset(visit,0,sizeof(visit));
            for (int i=0;i<N-1;i++) {
                int u,v;
                scanf("%d %d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            dfs(1);
            for (int i=1;i<=17;i++)
                for (int j=1;j<=N;j++) 
                    father[i][j]=father[i-1][father[i-1][j]];
    
            for (int i=0;i<M;i++) {
                int x,y;
                scanf("%d %d",&x,&y);
                f[x]++,f[y]++,f[lca(x,y)]-=2;
            }
            int ans=0;
            dfs2(1);
            for (int i=2;i<=N;i++) {
                if (f[i]==0) ans+=M;
                if (f[i]==1) ans++;
            }
            printf("%d
    ",ans); 
        }
        return 0;
    }
  • 相关阅读:
    快速了解layui中layer的使用
    导航栏切换按钮事件
    jQuery中 end(); 的用法
    JS中关于 一个关于计时器功能效果的实现
    js 中 setInterval 的返回值问题
    javascript 构造函数中的属性与原型上属性优先级的比较
    斐波那契数列 -- 递归算法(-)
    javascript 变量声明有var与无var 的区别
    作用域的理解--第一篇
    Javascript---数组常用方法
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/12528193.html
Copyright © 2020-2023  润新知