@codeforces - 1056G@ Take Metro

冷暖自知 提交于 2019-12-01 13:44:49

@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 本身内部结点个数是恒定的前提下。

因为有定时重构的缘故所以空间也不需要消耗特别大。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!