「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;
}
原文地址:https://www.cnblogs.com/crashed/p/15158691.html