• Solution -「CF 1056G」Take Metro


    (mathcal{Description})

      Link.

      有 (n) 个站台在一个圆环上,顺时针编号 (1sim n),其中 (1sim m) 号站台只能乘坐顺时针转的环线,其他车站只能乘坐逆时针转的环线。给定起点 (s) 和参数 (t),运动规则为:

    1. 乘坐在 (s) 站的环线,坐 (t) 站,令 (s) 为到达的站点;

    2. (tleftarrow t-1),若 (t>0),返回第一步。

      求 (s) 最终的值。

      (3le nle10^5)(1le tle 10^{12})

    (mathcal{Solution})

      想要倍增?参数 (t) 每时每刻在变化,不可能直接倍增。

      但注意到运动是在环上,所以 (t)((tmod n)) 在当前一步运动所达到的效果是等价的,而且 (n) 的范围能够接受,我们可以暴力模拟直到 (t) 成为 (n) 的倍数。

      接着再用倍增,我们需要求出从 (i~(i=1,2,cdots,n)) 出发,参数 (t=n),运动完成后到达的点。不放令所有点编号 (-1) 方便接下来使用 DP,令 (f(i,j)) 表示从 (i) 出发,参数 (t=j),运动完成后到达的点。显然有转移

    [f(i,j)=egin{cases} i&j=0\ f(i+j,j-1)&iin[0,m)land j>0\ f(i-j,j-1)&iin[m,n)land j>0 end{cases} ]

    其中第一维默认取(mod n) 意义下的非负值。可见,我们仅需要支持对 (f(i)) 这一维状态进行整段的提取和拼接,就能“整体 DP”出 (f(i+1)) 的状态。

      用可持久化 treap 维护这一过程即可。注意 DP 中提取出的两段区间可能有大面积重叠,需要使用随机合并的方法实现 merge 操作,并且定期重构树并回收掉所有用于持久化的结点。

      求出 (f(i,n)) 后,对其处理倍增数组即可回答询问。

      复杂度 (mathcal O(nlog t)),常数较大。

    (mathcal{Code})

    /* Clearink */
    
    #include <cstdio>
    #include <cassert>
    #include <cstdlib>
    
    #define rep( i, l, r ) for ( int i = l, rpbound##i = r; i <= rpbound##i; ++i )
    #define per( i, r, l ) for ( int i = r, rpbound##i = l; i >= rpbound##i; --i )
    
    typedef long long LL;
    
    const int MAXN = 1e5, MAXLG = 39;
    int n, m, s, f[MAXN + 5][MAXLG + 5], tf[MAXN + 5];
    LL turn;
    
    struct PersistentTreap {
    	static const int MAXND = 4e7;
    	int node, val[MAXND + 5], ch[MAXND + 5][2], siz[MAXND + 5];
    	// nodes's id are 0-based.
    
    	PersistentTreap() { srand( 20120712 ); }
    
    	inline int crtnd( const int v ) {
    		int u = node++;
    		val[u] = v, ch[u][0] = ch[u][1] = -1, siz[u] = 1;
    		return u;
    	}
    
    	inline void copy( const int u, const int v ) {
    		val[u] = val[v], ch[u][0] = ch[v][0], ch[u][1] = ch[v][1];
    		siz[u] = siz[v];
    	}
    
    	inline void pushup( const int u ) {
    		siz[u] = 1 + ( ~ch[u][0] ? siz[ch[u][0]] : 0 )
    			+ ( ~ch[u][1] ? siz[ch[u][1]] : 0 );
    	}
    
    	inline bool goleft( const int a, const int b ) {
    		return rand() % ( a + b ) < a;
    	}
    
    	inline int merge( const int u, const int v ) {
    		if ( !~u || !~v ) return ~u ? u : v;
    		int w = crtnd( 0 );
    		if ( goleft( siz[u], siz[v] ) ) {
    			copy( w, u ), ch[w][1] = merge( ch[w][1], v );
    		} else {
    			copy( w, v ), ch[w][0] = merge( u, ch[w][0] );
    		}
    		return pushup( w ), w;
    	}
    
    	inline void rsplit( const int u, const int k, int& x, int& y ) {
    		if ( !~u ) return void( x = y = -1 );
    		if ( !k || ( ~ch[u][0] && k <= siz[ch[u][0]] ) ) {
    			assert( node < MAXND );
    			copy( y = crtnd( 0 ), u );
    			rsplit( ch[y][0], k, x, ch[y][0] );
    			pushup( y );
    		} else {
    			assert( node < MAXND );
    			copy( x = crtnd( 0 ), u );
    			rsplit( ch[x][1],
    				k - ( ~ch[u][0] ? siz[ch[u][0]] : 0 ) - 1, ch[x][1], y );
    			pushup( x );
    		}
    	}
    
    	inline int rebuild( const int l, const int r, const int* arr ) {
    		if ( l > r ) return -1;
    		int mid = l + r >> 1, u = crtnd( arr[mid] );
    		ch[u][0] = rebuild( l, mid - 1, arr );
    		ch[u][1] = rebuild( mid + 1, r, arr );
    		return pushup( u ), u;
    	}
    
    	inline void travel( const int u, int& idx, int* arr ) {
    		if ( !~u ) return ;
    		travel( ch[u][0], idx, arr );
    		arr[idx++] = val[u];
    		travel( ch[u][1], idx, arr );
    	}
    } trp;
    
    inline int extract( const int rt, int l, int r ) {
    	l = ( l % n + n ) % n, r = ( r % n + n ) % n;
    	int x, y, z;
    	if ( l <= r ) {
    		trp.rsplit( rt, l, x, y );
    		assert( trp.siz[y] >= r - l + 1 );
    		trp.rsplit( y, r - l + 1, y, z );
    		return y;
    	} else {
    		trp.rsplit( rt, r + 1, x, y );
    		assert( trp.siz[y] >= l - r - 1 );
    		trp.rsplit( y, l - r - 1, y, z );
    		return trp.merge( z, x );
    	}
    }
    
    int main() {
    	scanf( "%d %d %d %lld", &n, &m, &s, &turn ), --s;
    	for ( ; turn % n; --turn ) {
    		s = ( s + ( s < m ? turn : -turn ) % n + n ) % n;
    	}
    	turn /= n;
    	int rt = -1;
    	rep ( i, 0, n - 1 ) rt = trp.merge( rt, trp.crtnd( i ) );
    	rep ( i, 1, n ) {
    		rt = trp.merge( extract( rt, i, i + m - 1 ),
    			extract( rt, m - i, n - i - 1 ) );
    		if ( !( i % 5000 ) ) {
    			int tmp = 0;
    			trp.travel( rt, tmp, tf );
    			trp.node = 0, rt = trp.rebuild( 0, tmp - 1, tf );
    		}
    	}
    	int tmp = 0;
    	trp.travel( rt, tmp, tf );
    	rep ( i, 0, n - 1 ) f[i][0] = tf[i];
    	for ( int j = 1; 1ll << j <= turn; ++j ) {
    		rep ( i, 0, n - 1 ) {
    			f[i][j] = f[f[i][j - 1]][j - 1];
    		}
    	}
    	for ( int j = 39; ~j; --j ) if ( ( turn >> j ) & 1 ) s = f[s][j];
    	printf( "%d
    ", s + 1 );
    	return 0;
    }
    
    
  • 相关阅读:
    艾氏筛法资料
    CF Soldier and Cards (模拟)
    CF Soldier and Badges (贪心)
    POJ 1308 Is It A Tree? (并查集)
    HDU 1272 小希的迷宫 (并查集)
    HDU 1213 How Many Tables (并查集)
    POJ 1611 The Suspects (并查集)
    POJ 2236 Wireless Network (并查集)
    Iptables-linux服务器做路由转发
    asdf
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14502044.html
Copyright © 2020-2023  润新知