1. 程式人生 > >題解-彈飛綿羊 (HNOI2015)

題解-彈飛綿羊 (HNOI2015)

bll include freopen != hnoi2015 style 來源 namespace tdi

LCT 模板題,分塊也很優秀。

分塊做法

維護每個點到第一次跳到下一個塊時的跳躍次數,並記錄其跳到下一個塊的第一個點。

註意常見的分塊玄學操作 n = min(sqrt(N), 100) 和 n = sqrt(N)*1.23 。

技術分享圖片
 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 }
View Code

LCT做法

技術分享圖片
  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 } 
View Code

NKOJ2381

P2381【HNOI2010】彈飛綿羊
時間限制 : 10000 MS 空間限制 : 265536 KB
問題描述

某天,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

題解-彈飛綿羊 (HNOI2015)