zoukankan      html  css  js  c++  java
  • [Turing Cup #3] 欧拉回路

    题目

    prob.pnglimits.png

    分析

    考虑某个子区间 ([l,r]) 为“好”的限制:

    • 对于每个点,其度数必须偶数;
    • 所有的边连通

    然后将它们转化到序列上:

    • 对于 (b_i),包含它的顺序对数量必须为偶数。这里的顺序对包括 (b_j<b_i,j<i)(b_i<b_k,i<k)​​ 两种;
    • 区间内不存在分界点 (m)​,使得 (min_{lle jle m}b_jgemax_{m<kle r }b_k)​ 且 ([l,m])​ 和 ((m,r])​​ 内均有边存在;

    考虑到 (nle 8000) 的数据范围,我们大可枚举每个区间,比如固定左端点之后移动右端点。

    首先处理第一条限制。将顺序对数量模 2 之后,有且仅有全 0 的情况才是符合要求的;为了快速判断,我们最好将多个 0/1 数码压缩为一个数。

    那么联想到一些经典的问题,我们可以对于每个数随机分配权值并进行运算;同时为了方便处理顺序对数量的改变,我们自然想到异或。于是,一种方法是,将所有顺序对数量模 2 为 1 的数的权值异或在一起,判断是否为 0。由于随机情况下,非全 0 的 01 串这般操作后还能得到 0 的概率很小,因此该方法简单且有效。

    接着处理第二条限制。注意到一段区间内没有连边当且仅当区间单调不增,故可以预处理 (lef_i,rig_i) 分别表示最小的 (j) 和最大的 (k) 使得 ([j,i],[i,k]) 内部没有连边。因此该限制可以被描述为:

    [max_{rig_lle m<lef_r-1}{min_{lle jle m}b_j-max_{m<kle r}b_k}<0 ]

    移动右端点的时候,(min_{lle jle m}b_j)​ 可以预处理,而 (max_{m<kle r}b_k)​ 可以使用单调栈维护。注意到对于栈内元素 (stk_k)​,当 (max b=b_{stk_k})​ 的时候,此时最大的 (min b)​ 在 (m=stk_{k-1})​,因此栈内的一个元素仅会贡献一次。由于 (rig_l)​ 确定了,变化的只有 (lef_r)​,那么合法的 (m)​ 被转化为一段前缀,扫描时处理好单调栈内前缀最大值。询问时可以直接在栈内二分,得到常数较小的 (O(n^2log_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( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    python实现清屏
    列表/字典/集合解析式和生成器
    SQL——pivot的用法
    前端的3大类描述
    2019-耦合性斗争笔记
    前端基础语法
    解决winform在win10下字体模糊的问题
    Xamarin.Android打包设置
    N0---我的编程教学提纲
    N0---关于变量
  • 原文地址:https://www.cnblogs.com/crashed/p/15115428.html
Copyright © 2011-2022 走看看