• 困难的图论


    题意

    给定由(n)个点(m)条边组成的无向连通图,保证没有重边和自环。
    你需要找出所有边,满足这些边恰好存在于一个简单环中。一个环被称为简单环,当且仅当
    它包含的所有点都只在这个环中被经过了一次。
    注意到这些边可能有很多条,你只需要输出他们编号的异或和即可。

    思路

    可以先从图中dfs出一棵生成树
    可知任一简单环一定包含一条非树边
    把玩样例可以发现,若一条边存在于一个简单环中,当且仅当它满足如下两个条件之一:

    • 此边为非树边,且它覆盖的树边不和任何其他非树边覆盖的树边有公共边
    • 此边为树边,且覆盖它的非树边满足上述条件

    于是我们可以对每一个非树边覆盖的树边进行链加,加完后枚举每一条非树边统计答案

    具体实现

    运用树上差分思想可以算出每条树边被多少条非树边覆盖,我们称它为(val[i])
    若此非树边所覆盖的树边的(val[i])均为(1),则可以累加
    那么如何判断和累加?可以用前缀来做(具体见代码)
    (还有一个玄皮的性质:环上的点在生成树上形成一条链,因此不用求(lca)
    即无向图生成树的非树边只有返祖边

    注意事项

    • 建双向边
    • 注意顺序,不要同一个简单环加两次

    不知为什么,交上去总是(RE)
    最后受不了开挂

    int size=40<<20;//40M
    __asm__ ("movq %0,%%rsp
    "::"r"((char*)malloc(size)+size));//网站开栈
    

    还发现一个有趣的东西:一般关于环的问题都要建出生成树,然后考虑树边与非树边的关系

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+50000;
    int n,m,tot,ans;
    int fi[N],ne[N<<1],to[N<<1],w[N<<1];
    int val[N],dep[N],s[N],asd[N];
    bool pd[N];
    inline int read()
    {
    	int s=0,w=1; char ch=getchar();
    	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
    	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
    	return s*w;
    }
    inline void add(int x,int y,int s)
    {
    	ne[++tot]=fi[x],fi[x]=tot,to[tot]=y,w[tot]=s;
    }
    void dfs1(int x,int pr)
    {
    	dep[x]=dep[pr]+1;
    	pd[x]=true;
    	for(int i=fi[x];i;i=ne[i])
    	{
    		int v=to[i];
    		if(v==pr) continue;
    		if(pd[v])
    		{
    			if(dep[v]>dep[x]) continue;//顺序问题
    			++val[x],--val[v];
    			continue;
    		}
    		dfs1(v,x);
    	}
    }
    void dfs2(int x,int pr)
    {
    	pd[x]=true;
    	for(int i=fi[x];i;i=ne[i])
    	{
    		int v=to[i];
    		if(v==pr||pd[v]) continue;
    		asd[v]=asd[x]^w[i];
    		dfs2(v,x);
    		val[x]+=val[v];
    	}
    }
    void dfs3(int x,int pr)
    {
    	pd[x]=true;s[x]=s[pr]+val[x];
    	for(int i=fi[x];i;i=ne[i])
    	{
    		int v=to[i];
    		if(v==pr) continue;
    		if(pd[v])
    		{
    			if(dep[v]>dep[x]) continue;
    			if((dep[x]-dep[v])==(s[x]-s[v]))
    				ans^=asd[x]^asd[v]^w[i];
    			continue;
    		}
    		dfs3(v,x);
    	}
    }
    int main()
    {
    	int size=40<<20;//40M
        __asm__ ("movq %0,%%rsp
    "::"r"((char*)malloc(size)+size));
    	n=read(),m=read();
    	for(int i=1;i<=m;++i)
    	{
    		int a=read(),b=read();
    		add(a,b,i);add(b,a,i);
    	}
    	dfs1(1,0);
    	for(int i=1;i<=n;++i)pd[i]=false;
    	dfs2(1,0);
    	for(int i=1;i<=n;++i)pd[i]=false;
    	dfs3(1,0);
    	cout<<ans;
    	exit(0);
    }
    
  • 相关阅读:
    Java面向对象——属性赋值的过程
    Java面向对象——类的成员之三:构造器(构造方法)constructor
    课后作业—5
    缓冲类的使用示例
    缓冲技术
    流的基类
    流的分类
    什么是流?
    关于开发中异常处理的建议
    阅读笔记-3
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/12112294.html
Copyright © 2020-2023  润新知