Live2D

Solution -「CF 1056G」Take Metro

Description

  Link.

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

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

  2. tt1,若 t>0,返回第一步。

  求 s 最终的值。

  3n1051t1012

Solution

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

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

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

f(i,j)={ij=0f(i+j,j1)i[0,m)j>0f(ij,j1)i[m,n)j>0

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

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

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

  复杂度 O(nlogt),常数较大。

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\n", s + 1 );
	return 0;
}

posted @   Rainybunny  阅读(102)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示