「Gym102956E」Brief Statements Union

题目

点这里看题目。

分析

我们用三元组 \((l,r,x)\) 表示令 \(a_l,a_{l+1},\dots,a_r\) 与起来为 \(x\) 的一条限制。

考虑给定一堆限制的时候,如何检查它们合不合法。

显然可以拆位考虑。枚举二进制的第 \(k\) 位,那么现在每个位置都只能是 0 或者 1。此时条件 \((l,r,x)\) 会变成一下两种之一:

  1. 如果 \(x\) 在第 \(k\) 位上为 1,此时这个条件要求 \(a_{l},a_{l+1},\dots,a_r\) 在第 \(k\) 位上与起来为 1,所以每个数必须都是 1;
  2. 如果 \(x\) 在第 \(k\) 位上位 0,此时这个条件要求 \(a_l,a_{l+1},\dots,a_r\) 在第 \(k\) 位上与起来为 0,所以其中至少有一个 0;

我们可以将所有的 1 限制先全部施加到序列上,再检查所有的 2 限制。既然可以检查了,我们就有了一个 \(O(n^2\log_2(\max x))\) 的算法。

但是,虽然外层枚举的内容可以被化为若干个限制的区间,但是区间之间并没有良好的递推关系(因为这个检查方式是强制离线的)。注意到问题的要求是“不考虑某一条限制,其余限制能否同时成立”,因此,这导出了另一个方向:我们可以根据每一位上的检查情况,推断不考虑哪些限制可以保证剩余成立

仍然枚举第 \(k\) 位,仍然先施加所有 1 限制。现在考虑 2 限制的冲突情况,假如有 \(c\) 条 2 限制出现了冲突:

  1. 如果 \(c=0\),那么当前位上任意删除一条限制都 OK;
  2. 如果 \(c=1\),那么我们删除冲突的那一条就 OK,但是删除其它的就不行;
  3. 如果 \(c>1\),怎么删除都不行;

考虑完 2 限制的删除方式,我们接着考虑 1 限制的。2 限制的要求是“给定区间内有至少一个 0”,为了让某一冲突的限制的区间中出现 0,我们考虑区间中仅仅被覆盖了 1 次的位置,其中一个位置为 \(i\)。如果删除 \(i\) 所对应的区间,那么就可以让这条冲突的限制变得合法。

那么对于一个 2 限制而言,我们需要做的是枚举其中所有仅被覆盖 1 次的位置,这些位置的 \(g\) 所对应的 1 限制就是删除后使得该 2 限制合法的所有 1 限制,我们不妨称为该 2 限制的“合法化 1 限制”。将所有的冲突的 2 限制的“合法化 1 限制”取交,我们得到的就是所有删除后可行的 1 限制。

由于如下性质:对于仅仅被覆盖 1 次的位置 \(i\),设覆盖它的区间为 \(g_i\)。考虑三个仅被覆盖 1 次的位置 \(i<j<k\),如果有 \(g_i=g_k\),那么一定会有 \(g_i=g_j=g_k\)(这容易使用反证法说明);所以我们可以对于可能的“合法化 1 限制”重标号,使得 \(g\) 是单调的。那么每个冲突的 2 限制的“合法 1 限制”在重标号后,下标将会变成一段连续的区间,而区间取交是相当容易的。同时,由于 \(g\) 的单调性,寻找某个冲突的 2 限制的“合法 1 限制”区间也比较简单。

精细实现后可以得到一个 \(O(n\log_2(\max a))\) 的算法。

小结:

  1. 一定要灵活转换思路,明知外层枚举的算法难以优化,就应该主动求变,舍弃掉这样的做法
  2. 思路不能僵化:对于“只忽略一条限制”的问题,不仅可以枚举,也可以推断
  3. 注意一下本题中利用标号的连续性质,将集合取交转化成区间取交的方法
  4. 注意一下实现细节:求 \(g\) 也可以使用差分。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

typedef long long LL;

const int MAXN = 1e6 + 5;

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‘ );
}

int vio[MAXN], tot;
int pos[MAXN], seq[MAXN], ID;

LL f[MAXN], g[MAXN];
int nxt[MAXN], pre[MAXN];

int ans[MAXN];

int qL[MAXN], qR[MAXN]; LL qX[MAXN];
int N, K;

int main()
{
	read( N ), read( K );
	rep( i, 1, K ) read( qL[i] ), read( qR[i] ), read( qX[i] );
	nxt[N + 1] = N + 1, pre[0] = 0;
	for( int k = 0 ; k < 60 ; k ++ )
	{
		rep( i, 1, N ) f[i] = g[i] = 0;
		rep( i, 1, K )
			if( qX[i] >> k & 1 )
			{
				f[qL[i]] ++, f[qR[i] + 1] --;
				g[qL[i]] += i, g[qR[i] + 1] -= i;
			}
		rep( i, 1, N ) f[i] += f[i - 1], g[i] += g[i - 1];
		per( i, N, 1 ) nxt[i] = f[i] ? nxt[i + 1] : i;
		ID = tot = 0;
		rep( i, 1, K )
			if( ! ( qX[i] >> k & 1 ) )
				if( nxt[qL[i]] > qR[i] )
					vio[++ tot] = i;
		if( tot == 0 )
		{
			rep( i, 1, K ) ans[i] ++;
			continue;
		}
		if( tot == 1 ) ans[vio[1]] ++;
		rep( i, 1, N ) 
			if( f[i] == 1 && ! pos[g[i]] )
				seq[pos[g[i]] = ++ ID] = g[i];
		rep( i, 1, N ) pre[i] = f[i] == 1 ? i : pre[i - 1];
		per( i, N, 1 ) nxt[i] = f[i] == 1 ? i : nxt[i + 1];
		int l = 0, r = ID;
		rep( i, 1, tot )
		{
			int u = vio[i];
			if( nxt[qL[u]] <= qR[u] )
				l = std :: max( l, pos[g[nxt[qL[u]]]] );
			else
				l = ID + 1;
			if( pre[qR[u]] >= qL[u] )
				r = std :: min( r, pos[g[pre[qR[u]]]] );
			else
				r = 0;
		}
		rep( i, l, r ) ans[seq[i]] ++;
		rep( i, 1, K ) pos[i] = 0;
	}
	rep( i, 1, K ) putchar( ( ans[i] == 60 ) + ‘0‘ );
	puts( "" );
	return 0;
}

「Gym102956E」Brief Statements Union

上一篇:对于指针的了解


下一篇:LVS负载均衡集群--DR模式