题解-弹飞绵羊 (HNOI2015)
LCT 模板题,分块也很优秀。
分块做法
维护每个点到第一次跳到下一个块时的跳跃次数,并记录其跳到下一个块的第一个点。
注意常见的分块玄学操作 n = min(sqrt(N), 100) 和 n = sqrt(N)*1.23 。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <algorithm> 3 #include <math.h> 4 5 using namespace std; 6 7 const int _N = 220000; 8 9 int N, n, M; 10 int A[_N], f[_N], des[_N]; 11 12 inline int dn(int v) { return (v-1)/n*n+1; } 13 14 inline int up(int v) { return min(N, (v-1)/n*n+n); } 15 16 int main() 17 { 18 int i; 19 scanf("%d", &N); 20 for (i = 1; i <= N; ++i) 21 scanf("%d", &A[i]); 22 scanf("%d", &M); 23 24 n = min(N, int(sqrt(N+0.5)*1.32)); 25 26 for (i = N; i >= 1; --i) 27 if (i+A[i] > up(i)) f[i] = 1, des[i] = min(i+A[i], N+1); 28 else f[i] = f[i+A[i]]+1, des[i] = des[i+A[i]]; 29 30 for (i = 1; i <= M; ++i) { 31 int ins, k; 32 scanf("%d%d", &ins, &k), ++k; 33 if (ins == 1) { 34 int cnt = 0, j = k; 35 while (j != N+1) { 36 cnt += f[j]; 37 j = des[j]; 38 } 39 printf("%d\n", cnt); 40 } else { 41 int j = k; 42 scanf("%d", &A[k]); 43 for (int t = j; t >= dn(j); --t) 44 if (t+A[t] > up(t)) f[t] = 1, des[t] = min(t+A[t], N+1); 45 else f[t] = f[t+A[t]]+1, des[t] = des[t+A[t]]; 46 } 47 } 48 49 return 0; 50 }
LCT做法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <stack> 3 4 using namespace std; 5 6 const int _N = 220000; 7 8 typedef long long LL; 9 10 stack<LL> S; 11 LL Sum[_N], L[_N], R[_N], Dad[_N], Rev[_N], J[_N], W[_N]; 12 13 inline void Update(LL x) { Sum[x] = Sum[L[x]] + W[x] + Sum[R[x]]; return; } 14 15 inline bool IsRoot(LL x) { return L[Dad[x]] != x && R[Dad[x]] != x; } 16 17 void Zig(LL x) 18 { 19 LL y = Dad[x], z = Dad[y]; 20 if (!IsRoot(y)) L[z] == y ? L[z] = x : R[z] = x; 21 Dad[x] = z; 22 L[y] = R[x], Dad[R[x]] = y; 23 R[x] = y, Dad[y] = x; 24 Update(y), Update(x); 25 return; 26 } 27 28 void Zag(LL x) 29 { 30 LL y = Dad[x], z = Dad[y]; 31 if (!IsRoot(y)) L[z] == y ? L[z] = x : R[z] = x; 32 Dad[x] = z; 33 R[y] = L[x], Dad[L[x]] = y; 34 L[x] = y, Dad[y] = x; 35 Update(y), Update(x); 36 return; 37 } 38 39 inline void PushDown(LL x) { 40 if (Rev[x]) { 41 Rev[L[x]] ^= 1, Rev[R[x]] ^= 1, Rev[x] ^= 1; 42 swap(L[x], R[x]); 43 } 44 return; 45 } 46 47 void Splay(LL x) 48 { 49 S.push(x); 50 for (LL i = x; !IsRoot(i); i = Dad[i]) S.push(Dad[i]); 51 while (!S.empty()) PushDown(S.top()), S.pop(); 52 53 while (!IsRoot(x)) { 54 LL y = Dad[x], z = Dad[y]; 55 if (!IsRoot(y)) { 56 if (L[z] == y) L[y] == x ? (Zig(y), Zig(x)) : (Zag(x), Zig(x)); 57 else R[y] == x ? (Zag(y), Zag(x)) : (Zig(x), Zag(x)); 58 } else { 59 L[y] == x ? Zig(x) : Zag(x); 60 } 61 } 62 63 return; 64 } 65 66 void Access(LL x) 67 { 68 LL t = 0; 69 while (x) { 70 Splay(x), R[x] = t, Update(x); 71 t = x, x = Dad[x]; 72 } 73 return; 74 } 75 76 LL FindRoot(LL x) { Access(x), Splay(x); while (L[x]) x = L[x]; return x; } 77 78 void MakeRoot(LL x) { Access(x), Splay(x), Rev[x] ^= 1; return; } 79 80 void Cut(LL x, LL y) { MakeRoot(x), Access(y), Splay(y), L[y] = Dad[x] = 0; Update(y); return; } 81 82 void Link(LL x, LL y) { MakeRoot(x), Dad[x] = y; } 83 84 int main() 85 { 86 // freopen("1.txt", "r", stdin); 87 LL N, Q, i, Suicide; 88 scanf("%lld", &N), Suicide = N+1; 89 // W[Suicide] = Sum[Suicide] = 1; 90 for (i = 1; i <= N; ++i) scanf("%lld", &J[i]), W[i] = 1, Sum[i] = 1; 91 for (i = 1; i <= N; ++i) 92 if (i+J[i] <= N) Link(i, i+J[i]); 93 else Link(i, Suicide); 94 95 scanf("%lld", &Q); 96 while (Q--) { 97 LL ins, t1, t2; 98 scanf("%lld%lld", &ins, &t1), ++t1; 99 if (ins == 1) { 100 MakeRoot(Suicide), Access(t1), Splay(t1); 101 printf("%lld\n", Sum[t1]); 102 } else if (ins == 2) { 103 scanf("%lld", &t2); 104 if (t1+J[t1] <= N) Cut(t1, t1+J[t1]); 105 else Cut(t1, Suicide); 106 J[t1] = t2; 107 if (t1+J[t1] <= N) Link(t1, t1+J[t1]); 108 else Link(t1, Suicide); 109 } 110 } 111 return 0; 112 }
NKOJ2381
P2381【HNOI2010】弹飞绵羊 | |
|
问题描述
某天,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。
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
输出格式
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
样例输入
4
1 2 1 1
3
1 1
2 1 1
1 1
样例输出
2
3
来源 HZOI