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;
    }
    
  • 相关阅读:
    mongodb
    python中读取文件的read、readline、readlines方法区别
    uva 129 Krypton Factor
    hdu 4734
    hdu 5182 PM2.5
    hdu 5179 beautiful number
    hdu 5178 pairs
    hdu 5176 The Experience of Love
    hdu 5175 Misaki's Kiss again
    hdu 5174 Ferries Wheel
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998434.html
Copyright © 2011-2022 走看看