1. 程式人生 > 其它 >[Turing Cup #3] 歐拉回路

[Turing Cup #3] 歐拉回路

題目

分析

考慮某個子區間 \([l,r]\) 為“好”的限制:

  • 對於每個點,其度數必須偶數;
  • 所有的邊連通

然後將它們轉化到序列上:

  • 對於 \(b_i\),包含它的順序對數量必須為偶數。這裡的順序對包括 \(b_j<b_i,j<i\)\(b_i<b_k,i<k\)​​ 兩種;
  • 區間內不存在分界點 \(m\)​,使得 \(\min_{l\le j\le m}b_j\ge\max_{m<k\le r }b_k\)​ 且 \([l,m]\)​ 和 \((m,r]\)​​ 內均有邊存在;

考慮到 \(n\le 8000\) 的資料範圍,我們大可列舉每個區間,比如固定左端點之後移動右端點。

首先處理第一條限制。將順序對數量模 2 之後,有且僅有全 0 的情況才是符合要求的;為了快速判斷,我們最好將多個 0/1 數碼壓縮為一個數。

那麼聯想到一些經典的問題,我們可以對於每個數隨機分配權值並進行運算;同時為了方便處理順序對數量的改變,我們自然想到異或。於是,一種方法是,將所有順序對數量模 2 為 1 的數的權值異或在一起,判斷是否為 0。由於隨機情況下,非全 0 的 01 串這般操作後還能得到 0 的概率很小,因此該方法簡單且有效。

接著處理第二條限制。注意到一段區間內沒有連邊當且僅當區間單調不增,故可以預處理 \(lef_i,rig_i\) 分別表示最小的 \(j\) 和最大的 \(k\) 使得 \([j,i],[i,k]\)

內部沒有連邊。因此該限制可以被描述為:

\[\max_{rig_l\le m<lef_r-1}\{\min_{l\le j\le m}b_j-\max_{m<k\le r}b_k\}<0 \]

移動右端點的時候,\(\min_{l\le j\le m}b_j\)​ 可以預處理,而 \(\max_{m<k\le r}b_k\)​ 可以使用單調棧維護。注意到對於棧內元素 \(stk_k\)​,當 \(\max b=b_{stk_k}\)​ 的時候,此時最大的 \(\min b\)​ 在 \(m=stk_{k-1}\)​,因此棧內的一個元素僅會貢獻一次。由於 \(rig_l\)

​ 確定了,變化的只有 \(lef_r\)​,那麼合法的 \(m\)​ 被轉化為一段字首,掃描時處理好單調棧內字首最大值。詢問時可以直接在棧內二分,得到常數較小的 \(O(n^2\log_2n)\)​;但注意到 \(lef\)​ 隨 \(r\)​ 單調不降,因此還可以維護指標,做到 \(O(n^2)\)​ 的複雜度。

小結:

  1. 隨機化權值用於判斷的方法很巧妙,也較通用,思路困住的時候可以多嘗試隨機化的方向;
  2. 維護單調棧額外資訊的方法值得注意。

程式碼

#include <ctime>
#include <cstdio>
#include <random>
#include <iostream>

#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 INF = 0x3f3f3f3f;
const int MAXN = 8005;

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 stk[MAXN], mx[MAXN], top;
int lef[MAXN], rig[MAXN], pref[MAXN];

bool deg[MAXN][MAXN];
int coe[MAXN][MAXN];

int A[MAXN], B[MAXN];
int N;

unsigned GetSeed() { char *tmp = new char; return time( 0 ) * ( unsigned long long ) tmp; }

int main()
{
	read( N );
	rep( i, 1, N ) read( A[i] );
	static std :: default_random_engine rng( GetSeed() );
	static std :: uniform_int_distribution<> gen( 1, 1e9 );
	rep( i, 1, N ) B[i] = gen( rng );
	rep( i, 1, N ) per( j, i, 1 )
	{
		deg[i][j] = deg[i][j + 1], coe[i][j] = coe[i][j + 1];
		if( A[j] < A[i] ) deg[i][j] ^= 1, coe[i][j] ^= B[j];
	}
	rep( i, 1, N ) lef[i] = i > 1 && A[i - 1] >= A[i] ? lef[i - 1] : i;
	per( i, N, 1 ) rig[i] = i < N && A[i] >= A[i + 1] ? rig[i + 1] : i;
	int ans = 0;
	rep( i, 1, N )
	{
		int val = 0;
		stk[top = 0] = rig[i] + 1, mx[0] = - INF;
		rep( j, i, N )
		{
			val ^= coe[j][i];
			if( deg[j][i] ) val ^= B[j];
			bool flg = val == 0;
			pref[j] = j == i ? A[j] : std :: min( pref[j - 1], A[j] );
			if( j > rig[i] + 1 )
			{
				for( ; top && A[stk[top]] <= A[j] ; top -- );	
				top ++, stk[top] = j, mx[top] = std :: max( mx[top - 1], pref[stk[top - 1]] - A[stk[top]] );
				if( rig[i] + 1 <= lef[j] - 2 )
				{
					int p = std :: lower_bound( stk + 1, stk + 1 + top, lef[j] - 1 ) - stk;
					int v = std :: max( mx[p - 1], pref[stk[p - 1]] - A[stk[p]] );
					flg &= v <= 0;
				}
			}
			ans += flg;
		}
	}
	write( ans ), putchar( '\n' );
	return 0;
}