CodeForces 1056 G(非旋可持久化treap)

传送门。

题解:

A n s ( x , t ) Ans(x,t) Ansx,t表示在x点,时间为t的答案

假设我们能预处理出所有 A n s ( x , n ) Ans(x,n) Ans(x,n),那么这题就很简单了。

考虑倒着想,假设现在倒着走了若干步,f[i]表示i到了哪里。

每次相当于从f中copy四段再接起来,那么用可持久化treap维护。

然后你发现treap的深度炸了:
Samjia的解释是分出的四段有相同的部分,随机就会炸。

似乎有点道理。

那干脆再加上个定期重构。

跑得飞快。

Code:

#include<ctime>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define pp printf
#define ll long long
#define ul unsigned long long
using namespace std;

const int N = 1e5 + 5;

const int M = N * 667;

int t[M][2], c[2], siz[M], tt, z[M], rt;

ul rd = 19260817;
ul rad() {
	rd *= 1000000007;
	return rd;
}
#define x0 t[x][0]
#define x1 t[x][1]
int xin(int &x) {
	if(!x) return 0;
	z[++ tt] = z[x]; siz[tt] = siz[x];
	t[tt][0] = t[x][0]; t[tt][1] = t[x][1];
	return x = tt;
}
void upd(int x) {
	if(x) siz[x] = siz[x0] + siz[x1] + 1;
}
void split(int x, int k) {
	if(k == siz[x]) {c[0] = x; c[1] = 0; return;}
	if(!k) { c[0] = 0; c[1] = x; return;}
	int z = k > siz[x0];
	split(xin(t[x][z]), k - z * (siz[x0] + 1));
	t[x][z] = c[!z]; c[!z] = x; upd(x);
}
int merge(int a, int b) {
	if(!a || !b) return a + b;
	if(rad() % (siz[a] + siz[b]) < siz[a]) {
		t[a][1] = merge(xin(t[a][1]), b);
		upd(a); return a;
	} else {
		t[b][0] = merge(a, xin(t[b][0]));
		upd(b); return b;
	}
}

int d[N];

void dg(int x) {
	if(!x) return;
	dg(x0); d[++ d[0]] = z[x]; dg(x1);
}

int fen(int x, int y) {
	int R = rt;
	split(xin(rt), x - 1);
	split(c[1], y - x + 1);
	rt = R;
	return c[0];
}

int n, m, st; ll tim;

int f[N];

int bz[N];

int dfs(int x, int y) {
	if(x > y) return 0;
	int m = x + y >> 1;
	z[++ tt] = d[m]; int p = tt;
	t[p][0] = dfs(x, m - 1);
	t[p][1] = dfs(m + 1, y);
	upd(p);
	return p;
}

int main() {
	srand(time (0));
	scanf("%d %d", &n, &m);
	fo(i, 1, n) d[i] = i;
	rt = dfs(1, n);
	fo(t, 1, n - 1) {
		int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
		c0 = fen(1 + t, min(m + t, n));
		if(m + t > n) c1 = fen(1, (m + t) % n);
		if(m + 1 - t < 1) c2 = fen(m + 1 - t + n, n);
		c3 = fen(max(1, m + 1 - t), n - t);
		rt = merge(c0, c1);
		c2 = merge(c2, c3);
		rt = merge(rt, c2);
		if(t % 5000 == 0) {
			d[0] = 0; dg(rt);
			rt = dfs(1, n);
		}
	}
	d[0] = 0; dg(rt);
	fo(i, 1, n) f[i] = d[i];
	scanf("%d %I64d", &st, &tim);
	int x = st;
	fd(i, tim % n, 1) {
		if(x <= m) x = (x + i - 1) % n + 1; else
		x = (x - i), x <= 0 ? x += n : 0;
	}
	ll c = tim / n;
	for(ll i = 1; i <= c; i ++) {
		if(bz[x]) {
			i = i + (c - i) / (i - bz[x]) * (i - bz[x]);
			while(i <= c) i ++, x = f[x];
			break;
		}
		bz[x] = i; x = f[x];
	}
	pp("%d\n", x);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值