[HNOI2010] 弹飞绵羊(LCT / 分块)

题目

描述

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

输入格式

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,
接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。

输出格式

对于每个i=1的情况,你都要输出一个需要的步数,占一行。

输入样例

4
1 2 1 1
3
1 1
2 1 1
1 1

输出样例

2
3

说明

对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000


解题思路

设绵羊在节点 n+1 n + 1 表示被“弹飞”,容易发现,如果我们把一个点与它的后继连起来的话,就形成了一颗树(每个节点都有唯一的后继,类同于树中每个节点都有唯一的父节点),然后修改操作就变成了删边和连边, 查询操作变成了查询两点之间距离——这就是一个LCT的模板题了。
复杂度 O(MlogN) O ( M l o g N )

但是,这道题还可以分块!
把原序列分成 n n 块,每块长为 n n ,每个节点记录需要弹多少次能到达下一个的块的哪个节点;修改时暴力修改块内所有与被修改节点相关的节点信息,查询时在块之间统计即可。
复杂度 O(MN)=O(4.48×107) O ( M N ) = O ( 4.48 × 10 7 ) ,可过!


Code#1 LCT

#include
#include

#define lson tr[id].son[0]
#define rson tr[id].son[1]

using namespace std;

const int N = 200005;
int n, a, b, c, m, k[N];

struct Splay{
    int son[2], fa, size;
    bool rev;
}tr[N];

struct OPT_LCT{
    int sta[N], top;
    inline void pushup(int id){
        tr[id].size = tr[lson].size + tr[rson].size + 1;
    }
    inline void pushdown(int id){
        if(tr[id].rev){
            if(lson){
                tr[lson].rev ^= 1;
                swap(tr[lson].son[0], tr[lson].son[1]);
            }
            if(rson){
                tr[rson].rev ^= 1;
                swap(tr[rson].son[0], tr[rson].son[1]);
            }
            tr[id].rev = 0;
        }
    }
    inline int which(int x){ return tr[tr[x].fa].son[1] == x; }
    inline int isRoot(int x){ return tr[tr[x].fa].son[0] != x && tr[tr[x].fa].son[1] != x; }
    inline void rotate(int x, int kind){
        int y = tr[x].fa, z = tr[y].fa, B = tr[x].son[kind];
        if(!isRoot(y))  tr[z].son[which(y)] = x;
        tr[x].son[kind] = y, tr[y].son[!kind] = B;
        tr[B].fa = y, tr[y].fa = x, tr[x].fa = z;
        pushup(y), pushup(x);
    }
    inline void splay(int x){
        sta[top = 1] = x;
        for(int i = x; !isRoot(i); i = tr[i].fa)    sta[++top] = tr[i].fa;
        while(top)  pushdown(sta[top--]);
        while(!isRoot(x)){
            int y = tr[x].fa, z = tr[y].fa;
            pushdown(z), pushdown(y), pushdown(x);
            int dir1 = !which(x), dir2 = !which(y);
            if(isRoot(y))   rotate(x, dir1);
            else{
                if(dir1 == dir2)    rotate(y, dir2);
                else    rotate(x, dir1);
                rotate(x, dir2);
            }
        }
    }
    inline void access(int x){
        int y = 0;
        while(x){
            splay(x);
            tr[x].son[1] = y;
            pushup(x);
            y = x, x = tr[x].fa;
        }
    }
    inline void makeRoot(int x){
        access(x), splay(x);
        tr[x].rev ^= 1;
        swap(tr[x].son[0], tr[x].son[1]);
        pushup(x);
    }
    inline void link(int x, int y){
        makeRoot(x), access(y), splay(y);
        tr[x].fa = y;
    }
    inline void cut(int x, int y){
        makeRoot(x), access(y), splay(y);
        tr[y].son[0] = tr[x].fa = 0;
        pushup(y);
    }
    inline int query(int x){
        makeRoot(n+1), access(x), splay(x);
        return tr[x].size - 1;
    }
}LCT;

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n + 1; i++) tr[i].size = 1;
    for(int i = 1; i <= n; i++){
        scanf("%d", &k[i]);
        LCT.link(i, i+k[i] > n ? n+1 : i+k[i]);
    }
    scanf("%d", &m);
    while(m--){
        scanf("%d%d", &a, &b);
        b++;
        if(a == 1)  printf("%d\n", LCT.query(b));
        else if(a == 2){
            scanf("%d", &c);
            LCT.cut(b, b+k[b] > n ? n+1 : b+k[b]);
            LCT.link(b, b+c > n ? n+1 : b+c);
            k[b] = c;
        }
    }
    return 0;
}

Code#2 分块

#include
#include
#include
#include

using namespace std;

inline int read(){
    int x = 0;
    bool fl = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')   fl = 0;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return fl ? x : -x;
}

const int N = 200005;
int n, q, k[N], opt, a, b, block[N<<1], ans, l[N], r[N];
struct G{
    int cnt, to;
}g[N];

int main(){
    memset(l, 0x7f, sizeof l);
    memset(r, -1, sizeof r);
    n = read();
    for(int i = 1; i <= n; i++){
        k[i] = read();
        block[i] = (i-1) / sqrt(n) + 1;
        l[block[i]] = min(l[block[i]], i);
        r[block[i]] = max(r[block[i]], i);
    }
    for(int i = 1; i <= n; i++){
        int now = i;
        while(block[now] == block[i]){
            g[i].cnt++;
            now += k[now];
        }
        g[i].to = now;
    }
    q = read();
    while(q--){
        opt = read(), a = read(); a++;
        if(opt == 1){
            ans = 0;
            int now = a;
            while(now <= n){
                ans += g[now].cnt;
                now = g[now].to;
            }
            printf("%d\n", ans);
        }
        else if(opt == 2){
            b = read();
            k[a] = b;
            for(int i = a; i >= l[block[a]]; i--){
                if(block[i] == block[i+k[i]]){
                    g[i].cnt = g[i+k[i]].cnt + 1;
                    g[i].to = g[i+k[i]].to;
                }
                else{
                    g[i].cnt = 1;
                    g[i].to = i + k[i];
                }
            }
        }
    }
    return 0;
}

你可能感兴趣的:(~算法集合,图论-树-LCT,分块)