@codeforces - 1056G@ Take Metro
@description@
环上有 n 个点,按顺时针顺序以 1 到 n 编号。其中 1~m 号点是红色的,m+1~n 号点时蓝色的。
一开始你位于点 s,并给定一个 t。
你需要重复以下步骤,直到 t = 0:
如果你所在结点为红色,顺时针移动 t 个点;否则逆时针移动 t 个点。然后 t 减一。
求最终所在的结点编号。
Input
第一行包含两个整数 n 和 m (3≤n≤10^5, 1≤m<n),含义见上。
第二行包含两个整数 s 和 t (1≤s≤n, 1≤t≤10^12),含义见上。
Output
仅输出一个整数,表示最终所在的结点编号。
Examples
Input
10 4
3 1
Output
4
Input
10 4
3 5
Output
4
@solution@
不难想到当 x > n 时,我们等价于走 x mod n 步(n 步相当于绕一个圈子)。
于是我们可以预先走 t mod n 步,之后每一次就走 n 步(即走 n, n - 1, ..., 1 步),重复 t / n 次。
我们可以预处理每个点走 n, n - 1, ..., 1 步所到达的目的地,然后就可以倍增了。
接着考虑怎么预处理每个点走 n, n - 1, ..., 1 步所到达的目的地。我们不妨大胆设一个 dp。
假如说正着设 dp 状态(即定义 dp[i][j] 表示从 i 出发走 n, ..., n - j 步所到达的目的地),我们的转移依赖于 dp[i][j-1],不好进行优化。
于是我们倒着设 dp 状态,即定义 dp[i][j] 表示从 i 出发走 j, ..., 1 步所到达的目的地。
这样子 dp[i][j] 在 i <= m 时等于 dp[i+j][j-1];在 i > m 时等于 dp[i-j][j-1](这里的 ±j 是在环的意义下进行加减)。
然后我们就可以优化这个 dp 了:注意它的转移总是 dp[][j-1] 的两个个区间进行平移得到 dp[][j]。
于是我们使用可持久化 treap,每次 dp 转移相当于先分裂出对应区间,再将区间合并。
有一个小 trick:可持久化 treap 中深度可能比较深,可以尝试将 merge 写成随机合并 + 定期重构(当然也不一定要加。。。这个看具体的人而定)。
@accepted code@
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
struct treap{
	struct node{
		int key, siz;
		node *ch[2];
	}pl[30*MAXN + 5], *ncnt, *NIL;
	typedef pair<node*, node*> Droot;
	treap() {
		NIL = &pl[0];
		NIL->ch[0] = NIL->ch[1] = NIL;
		NIL->key = NIL->siz = 0;
	}
	void trans(int *f, node *x, int k) {
		if( x == NIL ) return ;
		f[x->ch[0]->siz + k + 1] = x->key;
		trans(f, x->ch[0], k);
		trans(f, x->ch[1], x->ch[0]->siz + k + 1);
	}
	void pushup(node *x) {
		x->siz = x->ch[0]->siz + x->ch[1]->siz + 1;
	}
	node *build(int *f, int l, int r) {
		if( l > r ) return NIL;
		int mid = (l + r) >> 1;
		node *p = (++ncnt); p->key = f[mid];
		p->ch[0] = build(f, l, mid - 1);
		p->ch[1] = build(f, mid + 1, r);
		pushup(p);
		return p;
	}
	node *build(int *f, int n) {
		ncnt = &pl[0];
		return build(f, 1, n);
	}
	Droot split(node *x, int k) {
		if( x == NIL ) return make_pair(NIL, NIL);
		node *p = (++ncnt); (*p) = (*x);
		if( x->ch[0]->siz >= k ) {
			Droot q = split(x->ch[0], k);
			p->ch[0] = q.second; pushup(p);
			return make_pair(q.first, p);
		}
		else {
			Droot q = split(x->ch[1], k - x->ch[0]->siz - 1);
			p->ch[1] = q.first; pushup(p);
			return make_pair(p, q.second);
		}
	}//first k elements
	node *merge(node *x, node *y) {
		if( x == NIL ) return y;
		if( y == NIL ) return x;
		node *p = (++ncnt);
		if( rand() & 1 ) {
			(*p) = (*x), p->ch[1] = merge(x->ch[1], y);
			pushup(p);
		}
		else {
			(*p) = (*y), p->ch[0] = merge(x, y->ch[0]);
			pushup(p);
		}
		return p;
	}
}T;
treap::node *root;
int f[MAXN + 5];
int nxt[45][MAXN + 5];
int main() {
	int n, m, s; ll t;
	scanf("%d%d%d%lld", &n, &m, &s, &t);
	srand(20041112 ^ n ^ m ^ s);
	for(int i=1;i<=n;i++) f[i] = i;
	root = T.build(f, n);
	for(int i=1;i<=n;i++) {
		if( i % 300 == 0 )
			T.trans(f, root, 0), root = T.build(f, n);
		treap::node *p, *q;
		treap::node *tmp = T.split(root, i).second;
		if( tmp->siz >= m )
			p = T.split(tmp, m).first;
		else p = T.merge(tmp, T.split(root, m-tmp->siz).first);
		tmp = T.split(root, n - i).first;
		if( tmp->siz >= n-m )
			q = T.split(tmp, tmp->siz-(n-m)).second;
		else q = T.merge(T.split(root, n-((n-m)-tmp->siz)).second, tmp);
		root = T.merge(p, q);
	}
	T.trans(f, root, 0);
	while( t % n ) {
		if( s <= m ) s = (s - 1 + t) % n + 1;
		else s = ((s - 1 - t) % n + n) % n + 1;
		t--;
	}
	for(int i=1;i<=n;i++) nxt[0][i] = f[i];
	for(int j=1;j<45;j++)
		for(int i=1;i<=n;i++)
			nxt[j][i] = nxt[j-1][nxt[j-1][i]];
	int x = t/n;
	for(int i=44;i>=0;i--)
		if( (1LL<<i) & x ) s = nxt[i][s];
	printf("%d\n", s);
}
@details@
定时重构。。。感觉非常骚。。。
当然这是基于 treap 本身内部结点个数是恒定的前提下。
因为有定时重构的缘故所以空间也不需要消耗特别大。
@codeforces - 1056G@ Take Metro的更多相关文章
- Codeforces 1169A  Circle Metro
		题目链接:codeforces.com/contest/1169/problem/A 题意:有俩个地铁,一个从 1 → 2 → …→ n → 1→ 2 →…, 一个 从 n → n-1 →…→ 1 → ... 
- Codeforces Round #562 (Div. 2) A.Circle Metro
		链接:https://codeforces.com/contest/1169/problem/A 题意: The circle line of the Roflanpolis subway has n ... 
- Solution -「CF 1056G」Take Metro
		\(\mathcal{Description}\) Link. 有 \(n\) 个站台在一个圆环上,顺时针编号 \(1\sim n\),其中 \(1\sim m\) 号站台只能乘坐顺时针转的环 ... 
- wp已死,metro是罪魁祸首!
		1.这篇文章肯定会有类似这样的评论:“我就是喜欢wp,我就是喜欢metro,我就是软粉“等类似的信仰论者发表的评论. 2.2014年我写过一篇文章,windows phone如何才能在中国翻身? 我现 ... 
- MahApps.Metro打造拉风的桌面应用程序
		官网:http://mahapps.com/ github:https://github.com/MahApps/MahApps.Metro 
- MahApps.Metro使用
		# MahApps.Metro使用 # ## 下载MahApps.Metro ## PM> Install-Package MahApps.Metro ## MainWindow.xaml中添加 ... 
- python爬虫学习(5) —— 扒一下codeforces题面
		上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ... 
- 【Codeforces 738D】Sea Battle(贪心)
		http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ... 
- 【Codeforces 738C】Road to Cinema
		http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ... 
随机推荐
- idea小操作
			1.IDEA 实用功能Auto Import:自动优化导包(自动删除.导入包) 2.设置System.out.println();等快捷键 3.将idea的背景修改为图片 4.Linux ifconf ... 
- JavaScript创建对象的几种方式总结
			ECMA把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数. 1. 使用Object构造函数创建对象 创建自定义对象的最简单的方式就是创建一个Object的实例,然后再为它添加属性和方法 ... 
- LR11中web_save_timestamp_param
			时间戳是现在时间减去现在的时间 减去 1970年1月1日0点00 的时间 ,然后换算成毫秒. 所以我们需要借助 web_save_timestamp_param 来实现. web_save_times ... 
- gin入门-1
			Gin框架介绍 1. 简介Gin框架介绍A. 基于httprouter开发的web框架.http://github.com/julienschmidt/httprouterB. 提供Martini风格 ... 
- CesiumLab V1.3 新功能 MAX场景处理(免费Cesium处理工具集)
			每次到写文章的时候就很高兴,意味着又有重大功能更新了,也意味着10多天昏天黑地的闭关日子暂时结束了. 依照惯例,先放图 小范围精模型cesium加载效果 大范围白模cesium加载效果 ... 
- 字符串无法分割 split无效: java split()使用“.” “\” "|" "*" "+"要转义
			.是特殊字符 特殊字符需要转义. 改成split(“\\.”) 
- netbeans调试webapp 只能用localhost访问
			etbeans 我的电脑是192.168.0.2,用这个地址访问 网上有人说,分两种情况 此问题分两种情况: 1. 可以用127.0.0.1访问 2. 不能用127.0.0.1访问 针对第一种情况,我 ... 
- Mac系统常用快捷键大全
			苹果Mac系统常用快捷键有很多,但是很多童鞋对于这些mac快捷键都不是很熟悉,今天小编为大家整理了一份Mac系统常用快捷键大全,大家快收藏起来吧!平时在使用mac系统的时候可以提高不少工作效率哦! M ... 
- python基础--文件相关操作
			文件操作方式的补充: “+”表示的是可以同时读写某个文件 r+:可读可写 w+:可读可写 a+:可读可写 x:只写模式[不可读:不存在则创建,存在则报错] x+:可读可写 文件内的光标移动: 1.re ... 
- Leetcode8.String to Integer (atoi)字符串转整数(atoi)
			实现 atoi,将字符串转为整数. 该函数首先根据需要丢弃任意多的空格字符,直到找到第一个非空格字符为止.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字 ... 
