1. 程式人生 > >[HNOI2010]彈飛綿羊

[HNOI2010]彈飛綿羊

至少 char pri 題目 正整數 block 一個 con spa

題目描述

某天,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的情況,你都要輸出一個需要的步數,占一行。

輸入輸出樣例

輸入樣例#1: 復制
4
1 2 1 1
3
1 1
2 1 1
1 1
輸出樣例#1: 復制
2
3

說明

對於20%的數據n,m<=10000,對於100%的數據n<=200000,m<=100000


分成sqrt(n)塊,設f[i]為第i個位置跳f[i]次可以跳出當前塊, 設to[i]為第i個位置跳出當前塊到的位置;

查詢直接往後跳,修改暴力修改一個區間


#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
inline int read(){
    int res = 0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
    
return res; } const int N = 200005; int n, m; int k[N]; int f[N], to[N]; int Bl, block[N], L[N], R[N]; int main() { n = read();Bl = (int)sqrt((double)n); for (register int i = 1 ; i <= n ; i ++){ k[i] = read(); block[i] = (i - 1) / Bl + 1; R[block[i]] = Bl * block[i], R[block[i]] = min(R[block[i]], n); if(!L[block[i]]) L[block[i]] = i; } //for (int i=1;i<=n;i++)printf("%d %d\n", L[block[i]], R[block[i]]); for (register int i = n ; i >= 1 ; i --){ if (i + k[i] > R[block[i]]){ f[i] = 1, to[i] = i + k[i]; } else{ f[i] = f[i+k[i]] + 1, to[i] = to[i+k[i]]; } } //for(int i=1;i<=n;i++)printf("%d\n", to[i]); m = read(); while (m--) { int opt = read(); if (opt == 1){ // 詢問 int ans = 0; int p = read();p += 1; while(p <= n){ ans += f[p], p = to[p]; } printf("%d\n", ans); } else{ // 修改 int p = read(), kk = read();p+=1; k[p] = kk; for (register int i = R[block[p]] ; i >= L[block[p]] ; i --){ if (i + k[i] > R[block[p]]) { f[i] = 1, to[i] = i + k[i]; } else { f[i] = f[i+k[i]] + 1; to[i] = to[i+k[i]]; } } } } return 0; }

[HNOI2010]彈飛綿羊