• 「SCOI2016」萌萌哒


    题目

    点这里看题目。

    分析

    暴力:对应地合并取值必然相同的位置,可以用并查集维护。由于最终最高位非 0,所以的答案为 (9 imes 10^{ ext{连通块个数}-1})

    自然,我们需要优化这个过程。注意到我们总是对两段区间对应地合并,并且不存在在线的询问,这意味着进行标记的处理,将一些合并信息储存在较大的块内部延后处理。

    由于块表示“对应合并”,这不可避免地要求合并的块大小相等,因此我们可以使用倍增来构造所需的块。也即,我们用 ((i,j)) 来表示 ([i,i+2^j)) 这个区间,并对 ((x,j),1le xle n-2^j+1) 维护并查集。合并的时候对长度进行二进制拆分,对应的块用并查集合起来即可。

    回归块的作用,其实用块表示“区间对应合并”的标记,因此我们需要在最后做下方标记的操作:对于第 (j) 层的每对父子,将它们劈成前后两半,两半在第 (j-1) 层对应合并即可。由于并查集维护的是图的生成树,因此这样可以保证连通性,不必担心标记丢失的问题。

    小结:

    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 mod = 1e9 + 7;
    const int MAXN = 1e5 + 5, MAXLOG = 17;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( ! ( '0' <= s && s <= '9' ) ) { 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 UFS
    {
    	int fa[MAXN];
    	
    	UFS(): fa{} {}
    	
    	void MakeSet( const int n ) { rep( i, 1, n ) fa[i] = i; }
    	int FindSet( const int u ) { return fa[u] = ( fa[u] == u ? u : FindSet( fa[u] ) ); }
    	void UnionSet( const int u, const int v ) { fa[FindSet( u )] = FindSet( v ); }
    };
    
    UFS conn[MAXLOG];
    
    int N, M, lg2;
    
    int main()
    {
    	read( N ), read( M ), lg2 = log2( N ); 
    	rep( i, 0, lg2 ) conn[i].MakeSet( N - ( 1 << i ) + 1 );
    	while( M -- )
    	{
    		int l1, r1, l2, r2;
    		read( l1 ), read( r1 ), read( l2 ), read( r2 );
    		int len = r1 - l1 + 1;
    		per( i, lg2, 0 ) if( len >> i & 1 )
    		{
    			conn[i].UnionSet( l1, l2 );
    			l1 += 1 << i, l2 += 1 << i;
    		}
    	}
    	per( i, lg2, 1 )
    		rep( j, 1, N - ( 1 << i ) + 1 )
    		{
    			if( conn[i].fa[j] == j ) continue;
    			int u = j, v = conn[i].fa[j];
    			conn[i - 1].UnionSet( u, v );
    			conn[i - 1].UnionSet( u + ( 1 << i - 1 ), v + ( 1 << i - 1 ) );
    		}
    	int cnt = 0;
    	rep( i, 1, N ) cnt += conn[0].fa[i] == i;
    	int ans = 9;
    	rep( i, 2, cnt ) ans = 10ll * ans % mod;
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    文件读取原理和文件删除原理
    day 1 随堂作业
    day 2 随堂作业
    day 3 随堂作业
    day 4 随堂作业
    day 5 随堂作业
    day 6 随堂作业
    day 7 随堂作业
    day 8 随堂作业
    day 9 随堂作业
  • 原文地址:https://www.cnblogs.com/crashed/p/15158691.html
Copyright © 2020-2023  润新知