BZOJ2002 [HNOI2010]Bounce 弹飞绵羊

58 篇文章 0 订阅

Address


Solution

  • LCT L C T 模板题……
  • 因为原图无环,把位置看做 n n 个点,用 LCT 维护各点 sze s z e (子树大小)。
  • 新增一个表示被弹飞的点 n+1 n + 1 ,则对于位置 i i
    • i+kin,连边 (i,i+ki) ( i , i + k i )
    • i+ki>n i + k i > n ,连边 (i,n+1) ( i , n + 1 )
    • 对于初始时的 ki k i 先连接上边
    • 对于修改操作:断开原来的边再连接上新的边
    • 对于询问操作:取出 LCT L C T i i ~ n+1 的实路径,输出顶部节点 sze s z e
    • 时间复杂度 均摊 O(nlogn) O ( n log ⁡ n )
    • Code

      #include <iostream>
      #include <cstdio>
      #include <cctype>
      #include <algorithm>
      #include <cstring>
      
      using namespace std;
      
      namespace inout
      {
          const int S = 1 << 20;
          char frd[S], *ihed = frd + S;
          const char *ital = ihed;
      
          inline char inChar()
          {
              if (ihed == ital)
                  fread(frd, 1, S, stdin), ihed = frd;
              return *ihed++;
          }
      
          inline int get()
          {
              char ch; int res = 0; bool flag = false;
              while (!isdigit(ch = inChar()) && ch != '-');
              (ch == '-' ? flag = true : res = ch ^ 48);
              while (isdigit(ch = inChar()))
                  res = res * 10 + ch - 48;
              return flag ? -res : res; 
          }
      
          char fwt[S], *ohed = fwt;
          const char *otal = fwt + S;
      
          inline char outChar(char ch)
          {
              if (ohed == otal)
                  fwrite(fwt, 1, S, stdout), ohed = fwt;
              return *ohed++ = ch;
          }
      
          inline void put(int x)
          {
              if (x > 9) put(x / 10);
              outChar(x % 10 + 48);
          }
      };
      using namespace inout;
      
      const int N = 2e5 + 5;
      int n, m, k[N];
      
      inline int Min(int x, int y) {return x < y ? x : y;}
      
      namespace LCT
      {   
          int lc[N], rc[N], fa[N], sze[N], rev[N];
          int qr, que[N];
      
          inline bool Which(int x)
          {
              return lc[fa[x]] == x;
          }
      
          inline bool isRoot(int x)
          {
              return !fa[x] || lc[fa[x]] != x && rc[fa[x]] != x;
          }
      
          inline bool Uptdate(int x)
          {
              sze[x] = sze[lc[x]] + sze[rc[x]] + 1;
          } 
      
          inline void pushDown(int x)
          {
              if (rev[x])
              {
                  swap(lc[x], rc[x]);
                  if (lc[x]) rev[lc[x]] ^= 1;
                  if (rc[x]) rev[rc[x]] ^= 1;
                  rev[x] = 0;
              }
          }
      
          inline void Rotate(int x)
          {
              int y = fa[x], z = fa[y],
                  b = lc[y] == x ? rc[x] : lc[x];
              if (z && !isRoot(y)) 
                  (lc[z] == y ? lc[z] : rc[z]) = x;
              fa[x] = z; fa[y] = x; 
              if (b) fa[b] = y;
              if (lc[y] == x) rc[x] = y, lc[y] = b;
                  else lc[x] = y, rc[y] = b;
              Uptdate(y);
          }
      
          inline void Splay(int x)
          {
              que[qr = 1] = x;
              for (int y = x; !isRoot(y); y = fa[y]) que[++qr] = fa[y];
              for (int i = qr; i; --i) pushDown(que[i]);
      
              while (!isRoot(x))
              {
                  if (!isRoot(fa[x]))
                      Which(fa[x]) == Which(x) ? Rotate(fa[x]) : Rotate(x);
                  Rotate(x);
              }
              Uptdate(x);
          }
      
          inline void Access(int x)
          {
              for (int y = 0; x; y = x, x = fa[x])
              {
                  Splay(x); rc[x] = y;
                  if (y) fa[y] = x; Uptdate(x);
              }
          }
      
          inline int findRoot(int x)
          {
              Access(x); Splay(x);
              while (pushDown(x), lc[x]) x = lc[x];
              Splay(x); return x;
          }
      
          inline void makeRoot(int x)
          {
              Access(x); Splay(x); rev[x] ^= 1;
          }
      
          inline void Link(int x, int y)
          {
              makeRoot(x); fa[x] = y;
          }
      
          inline void Cut(int x, int y)
          {
              makeRoot(x); Access(y); Splay(y);
              lc[y] = fa[x] = 0; Uptdate(y);
          }
      
          inline int Select(int x, int y)
          {
              makeRoot(x); Access(y); Splay(y);
              //把x置为根后,标记未下传,影响询问结果,因此Splay(y) 
              return sze[y] - 1;
          }
      };
      using namespace LCT;
      
      int main()
      {
      //  freopen("bounce.in", "r", stdin);
      //  freopen("bounce.out", "w", stdout);
      
          n = get() + 1;
          for (int i = 1; i <= n; ++i) sze[i] = 1;
          for (int i = 1; i < n; ++i) 
              k[i] = get(), Link(i, Min(i + k[i], n));
      
          m = get(); int x, y;
          while (m--)
          {   
              x = get(); y = get() + 1;
              if (x & 1)
                  put(Select(y, n)), outChar('\n');
              else 
              {
                  Cut(y, Min(y + k[y], n)); 
                  k[y] = get();
                  Link(y, Min(y + k[y], n));
              }
          }
          fwrite(fwt, 1, ohed - fwt, stdout);
      
      //  fclose(stdin); fclose(stdout);
          return 0;
      }
      
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值