zoukankan      html  css  js  c++  java
  • 【POJ3417】Network【LCA】【差分】

    题目大意:

    题目链接:http://poj.org/problem?id=3417
    一个nn个结点的树加入了mm条边。求删除一条原边和一条新边把这个图拆成两个部分的方案数。


    思路:

    对于后面的mm条新边,我们可以把每一条原边加入一个边权(初始为00),对于每次加入的新边(x,y)(x,y),我们就将树上从xxyy的路径的边权加11。最终求答案时枚举所有的边:

    • 如果边权为00,那么删除这条边之后树就被分成了两个不相邻的部分。那么此时删除任意一条边都满足分成两个部分。ans+=mans+=m
    • 如果边权为11,那么删除包含这条边的新边(有且仅有一条)就可以满足分成两个部分。ans++ans++
    • 如果边权大于11,无论如何都不可以满足要求。

    那么我们就得到了一个O(nm)O(nm)的算法。


    关于优化

    记得差分吗?
    我们每次将新边(x,y)(x,y)之间的每一条边的权值加11,其实是可以利用树上差分的方法来O(1)O(1)搞出来的。
    对于新边(x,y)(x,y),我们将s[x]++,s[y]++s[lca(x,y)]=2s[x]++,s[y]++,s[lca(x,y)]-=2。然后跑一遍DFSDFS,求出每一点s[x]s[x]表示它与父节点之间的边被覆盖的次数。
    时间复杂度O(n+m)O(n+m)


    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std; 
    typedef long long ll;
    
    const int N=100010;
    const int LG=20;
    int n,m,x,y,tot,head[N],s[N],f[N][LG+1],dep[N];
    ll ans;
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot].to=to;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void dfs1(int x,int fa)
    {
    	dep[x]=dep[fa]+1;
    	f[x][1]=fa;
    	for (int i=2;i<=LG;i++)
    		f[x][i]=f[f[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int y=e[i].to;
    		if (fa==y) continue;
    		dfs1(y,x);
    	}
    }
    
    int lca(int x,int y)
    {
    	if (dep[x]<dep[y]) swap(x,y);
    	for (int i=LG;i>=1;i--)
    		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if (x==y) return x;
    	for (int i=LG;i>=1;i--)
    		if (f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][1];
    }
    
    int dfs2(int x,int fa)
    {
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int y=e[i].to;
    		if (fa==y) continue;
    		s[x]+=dfs2(y,x);
    	}
    	if (x!=1)
    	{
    		if (s[x]==0) ans+=(ll)m;
    		if (s[x]==1) ans++;
    	}
    	return s[x];
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	dfs1(1,0);
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		s[x]++;
    		s[y]++;
    		s[lca(x,y)]-=2;
    	}
    	dfs2(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    UE4 Abc 批量导入
    UE4源码摘录(424)
    JZ10 矩形覆盖
    JZ27 字符串的排列
    JZ66 机器人的运动范围
    JZ65 矩阵中的路径
    JZ12 数值的整数次方
    JZ37 数字在升序数组中出现的次数
    JZ6 旋转数组的最小数字
    JZ67 剪绳子
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998435.html
Copyright © 2011-2022 走看看