zoukankan      html  css  js  c++  java
  • [AGC035B]Even Degrees

    题目

    点这里看题目。

    分析

    参考混合图欧拉回路的想法,不难想到初始给每条边随机定向。

    这样如果有奇数个奇度点,就肯定无解;具体怎么证,我也不知道

    接着也可以想到,由于原图连通,所以对于任意两个奇度点,都必然有一条路径连接这两个点。反转这条路径上所有边的方向,中途的点出度的奇偶性不变,而两端的点的奇偶性恰好反转。

    因此可以给奇度点任意匹配,接着反转路径。由于任何的路径都是 OK 的,因此可以直接找生成树上的路径。所以我们可以对每对奇度点在树上对边进行差分,确定最终的边的方向。

    小结:

    1. 初始一组任意解并进行调整的方法非常常用
    2. 反转路径上的边这一操作的度数性质比较常见;

    代码

    #include <cmath>
    #include <cstdio>
    
    #define rep( i, a, b ) for( int (i) = (a) ; (i) <= (b) ; ++ (i) )
    #define per( i, a, b ) for( int (i) = (a) ; (i) >= (b) ; -- (i) )
    
    const int MAXN = 1e6 + 5, MAXLOG = 20;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); }
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    struct Edge
    {
    	int to, nxt, id;
    }Graph[MAXN];
    
    int seq[MAXN];
    
    int fr[MAXN], to[MAXN];
    bool rev[MAXN];
    
    int f[MAXN][MAXLOG];
    int head[MAXN], deg[MAXN], fa[MAXN], tag[MAXN], dep[MAXN];
    int N, M, cnt = 1, lg2;
    bool vis[MAXN], pair[MAXN];
    
    void MakeSet( const int siz ) { rep( i, 1, siz ) fa[i] = i; }
    int FindSet( const int u ) { return fa[u] = ( fa[u] == u ? u : FindSet( fa[u] ) ); }
    
    bool UnionSet( int u, int v ) 
    {
    	u = FindSet( u ), v = FindSet( v );
    	if( u == v ) return false; fa[u] = v; return true;
    }
    
    void AddEdge( const int from, const int to, const int I )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	Graph[cnt].id = I, head[from] = cnt;
    }
    
    void Init( const int u, const int fa )
    {
    	f[u][0] = fa, dep[u] = dep[fa] + 1;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa ) Init( v, u );
    }
    
    void Init()
    {
     	Init( 1, 0 ), lg2 = log2( N );
     	rep( j, 1, lg2 ) rep( i, 1, N ) f[i][j] = f[f[i][j - 1]][j - 1];
    }
    
    void Balance( int &u, int stp )
    {
    	for( int i = 0 ; ( 1 << i ) <= stp ; i ++ )
    		if( stp & ( 1 << i ) ) u = f[u][i];
    }
    
    int LCA( int u, int v )
    {
    	if( dep[u] > dep[v] ) Balance( u, dep[u] - dep[v] );
    	if( dep[u] < dep[v] ) Balance( v, dep[v] - dep[u] );
    	if( u == v ) return u;
    	rep( i, lg2, 0 ) if( f[u][i] ^ f[v][i] ) u = f[u][i], v = f[v][i];
    	return f[u][0];
    }
    
    void Recovery( const int u, const int fa )
    {
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    		{
    			Recovery( v, u ), tag[u] += tag[v];
    			rev[Graph[i].id] = tag[v] & 1;
    		}
    }
    
    int main()
    {
    	read( N ), read( M );
    	MakeSet( N );
    	rep( i, 1, M ) 
    	{
    		read( fr[i] ), read( to[i] ), deg[fr[i]] ++;
    		if( UnionSet( fr[i], to[i] ) ) AddEdge( fr[i], to[i], i ), AddEdge( to[i], fr[i], i );
    	}
    	int tot = 0;
    	rep( i, 1, N ) if( deg[i] & 1 ) seq[++ tot] = i;
    	if( tot & 1 ) return puts( "-1" ), 0;
    	Init(); int half = tot >> 1;
    	rep( i, 1, half ) 
    		tag[seq[i]] ++, tag[seq[i + half]] ++, tag[LCA( seq[i], seq[i + half] )] -= 2;
    	Recovery( 1, 0 );
    	rep( i, 1, M )
    		if( rev[i] ) printf( "%d %d
    ", to[i], fr[i] );
    		else printf( "%d %d
    ", fr[i], to[i] );
    	return 0;
    }
    
  • 相关阅读:
    硬件开源为什么如此之难?
    传智播客C++
    为什么我们要在指针前面加一个数据类型来限定那?
    天津大学仁爱学院教务网、图书馆以及数字化平台网址
    关于小米手机USB传输稍大点的文件老中断的问题解决方法!
    关于接地:数字地、模拟地、信号地、交流地、直流地、屏蔽地、浮地
    关于
    Android-APK签名
    Android-Activity跳转时动画
    Android-GridView 滑动条设置一直显示状态
  • 原文地址:https://www.cnblogs.com/crashed/p/14267820.html
Copyright © 2011-2022 走看看