zoukankan      html  css  js  c++  java
  • [HNOI2010]弹飞绵羊

    Description

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

    Input

    第一行包含一个整数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

    Output

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

    Sample Input

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

    Sample Output

    2
    3

    先建树,如果i+k大于n,就和n+1,建边表示弹出,否则和i+k建边

    建树用LCT的link操作

    修改就cut操作删掉旧边,加上新边

    查询时,把n+1变为树根,把x到n+1都变为实根

    查询路径上节点数-1输出

    splay维护size,因为总是只有一颗完整的树,所以不需要判断cut和link操作的可行性

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 using namespace std;
      7 int size[200005],ch[200005][2],rev[200005],pre[200005],isrt[200005],n,m,k[200005];
      8 void pushup(int o)
      9 {
     10   if (!o) return;
     11   size[o]=size[ch[o][0]]+size[ch[o][1]]+1;
     12 }
     13 void pushdown(int o)
     14 {
     15   if (!o) return;
     16   if (rev[o])
     17     {
     18       int ls=ch[o][0],rs=ch[o][1];
     19       rev[ls]^=1;
     20       swap(ch[ls][0],ch[ls][1]);
     21       rev[rs]^=1;
     22       swap(ch[rs][0],ch[rs][1]);
     23       rev[o]=0;
     24     }
     25 }
     26 void push(int o)
     27 {
     28   if (isrt[o]==0) push(pre[o]);
     29   pushdown(o);
     30 }
     31 void rotate(int o,bool kind)
     32 {
     33   int p=pre[o];
     34   ch[p][!kind]=ch[o][kind];pre[ch[o][kind]]=p;
     35   if (isrt[p]) isrt[p]=0,isrt[o]=1;
     36   else ch[pre[p]][ch[pre[p]][1]==p]=o;
     37   pre[o]=pre[p];
     38   ch[o][kind]=p;pre[p]=o;
     39   pushup(p);pushup(o);
     40 }
     41 void splay(int o)
     42 {
     43   push(o);
     44   while (isrt[o]==0)
     45     {
     46       if (isrt[pre[o]])
     47     rotate(o,ch[pre[o]][0]==o);
     48       else
     49     {
     50       int p=pre[o],kind=ch[pre[p]][0]==p;
     51       if (ch[p][kind]==o)
     52         rotate(o,!kind),rotate(o,kind);
     53       else rotate(p,kind),rotate(o,kind);
     54     }
     55     }
     56 }
     57 void access(int o)
     58 {
     59   int y=0;
     60   while (o)
     61     {
     62       splay(o);
     63       isrt[ch[o][1]]=1;
     64       isrt[ch[o][1]=y]=0;
     65       pushup(o);
     66       y=o;o=pre[o];
     67     }
     68 }
     69 void makeroot(int o)
     70 {
     71   access(o);
     72   splay(o);
     73   rev[o]^=1;
     74   swap(ch[o][0],ch[o][1]);
     75 }
     76 void link(int x,int y)
     77 {
     78   makeroot(x);
     79   pre[x]=y;
     80 }
     81 void cut(int x,int y)
     82 {
     83   makeroot(x);
     84   access(y);splay(y);
     85   ch[y][0]=0;pre[x]=0;
     86   isrt[x]=1;
     87   pushup(y);
     88 }
     89 int main()
     90 {int i,c,x,y,st,ed;
     91   cin>>n;
     92   for (i=1;i<=n+1;i++)
     93     isrt[i]=1,size[i]=1;
     94   for (i=1;i<=n;i++)
     95     {
     96       scanf("%d",&k[i]);
     97       if (i+k[i]>n) link(i,n+1);
     98       else link(i,i+k[i]);
     99     }
    100   cin>>m;
    101   for (i=1;i<=m;i++)
    102     {
    103       scanf("%d",&c);
    104       if (c==1)
    105     {
    106       scanf("%d",&x);x++;
    107       makeroot(n+1);
    108       access(x);
    109       splay(x);
    110       printf("%d
    ",size[x]-1);
    111     }
    112       else
    113     {
    114       scanf("%d%d",&x,&y);x++;
    115       if (k[x]+x>n) st=n+1;
    116       else st=k[x]+x;
    117       if (y+x>n) ed=n+1;
    118       else ed=y+x;
    119       k[x]=y;
    120       if (st!=ed)
    121         {
    122           cut(x,st);
    123           link(x,ed);
    124         }
    125     }
    126     }
    127 }
    View Code LCT

    还有分块的做法

    把n个点分成$sqrt n$块

    预处理出f[i],s[i]

    f[i]表示i会跳到下一块的地方

    s[i]表示i跳到f[i]所需要的步数

    查询时直接往后跳,最多跳$sqrt n$块

    修改时只修改那一块$sqrt n$个元素

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 int n,k[300001],lim,s[300001],f[300001],m;
     8 int main()
     9 {int i,j,opt,x,now,cnt,y;
    10   cin>>n;
    11   for (i=1;i<=n;i++)
    12     {
    13       scanf("%d",&k[i]);
    14     }
    15   lim=sqrt(n);
    16   for (i=1;i<=n;i+=lim)
    17     {
    18       for (j=min(n,i+lim-1);j>=i;j--)
    19     {
    20       if (k[j]+j>min(n,i+lim-1)) s[j]=1,f[j]=k[j]+j;
    21       else s[j]=s[j+k[j]]+1,f[j]=f[k[j]+j];
    22     }
    23     }
    24   cin>>m;
    25   for (i=1;i<=m;i++)
    26     {
    27       scanf("%d%d",&opt,&x);x++;
    28       if (opt==1)
    29     {
    30       cnt=0;
    31       while (x<=n)
    32         {
    33           cnt+=s[x];
    34           x=f[x];
    35         }
    36       printf("%d
    ",cnt);
    37     }
    38       else
    39     {
    40       scanf("%d",&y);
    41       k[x]=y;
    42       if (x%lim==0) now=x-lim+1;
    43       else now=x-x%lim+1;
    44       for (j=min(n,now+lim-1);j>=now;j--)
    45         {
    46           if (k[j]+j>min(n,now+lim-1)) s[j]=1,f[j]=k[j]+j;
    47           else s[j]=s[j+k[j]]+1,f[j]=f[k[j]+j];
    48         }
    49     }
    50     }
    51 }
    View Code分块
  • 相关阅读:
    Android Studio轻松上手指南(1)
    在MyEclipse下创建Java Web项目 入门(图文并茂)经典教程
    opencv提取截获图像(总结摘来)
    10.正则表达式匹配
    遍历 Map 的方式
    09.回文数
    08.字符串转换位整数
    07.整数反转
    06. Z字型变换
    05. 求最长回文子串
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/8297285.html
Copyright © 2011-2022 走看看