[Turing Cup #3] 歐拉回路
阿新 • • 發佈:2021-08-08
題目
分析
考慮某個子區間 \([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]\)
移動右端點的時候,\(\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\)
小結:
- 隨機化權值用於判斷的方法很巧妙,也較通用,思路困住的時候可以多嘗試隨機化的方向;
- 維護單調棧額外資訊的方法值得注意。
程式碼
#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;
}