计算复杂度为 Θ ( 2 ∗ n n ) \Theta(2*n\sqrt{n}) Θ(2∗nn)的代码被卡到了 30 30 30分(大概是用了 S T L STL STL吧)。在 b r z brz brz的提醒下写了一个逆向 d p dp dp,复杂度为 Θ ( n n ) \Theta(n\sqrt{n}) Θ(nn),竟然神奇地过了。
后来又写了一个 L C T LCT LCT,跑得好像差不多。
https://www.luogu.org/problemnew/show/P3203
n n n个位置,每个位置有一个弹跳系数(正整数),支持修改,求当前询问的点多少次能跳出这 n n n个位置。
暴力分块。
记录两个东西:当前点跳出当前块的步数以及跳到的点。
可以逆向 d p dp dp得出这两个东西。
于是就把正解为 L C T LCT LCT的省选题过了。
#include
#include
#include
#include
using namespace std;
queue<int> f;
int n,q,block;
struct node{int x,d,end;} a[500010];
int belong[500010],lazy[500010],l[500010],r[500010];
void init()
{
for(int i=1;i<=belong[n];i++)
for(int j=r[i];j>=l[i];j--)
if(j+a[j].x>r[i]) a[j].d=1,a[j].end=j+a[j].x; else a[j].d=a[j+a[j].x].d+1,a[j].end=a[j+a[j].x].end;
}
void work(int x)
{
for(int i=x;i>=l[belong[x]];i--)
if(i+a[i].x>r[belong[x]]) a[i].d=1,a[i].end=i+a[i].x; else a[i].d=a[i+a[i].x].d+1,a[i].end=a[i+a[i].x].end;
}
int solve(int x)
{
int ans=0;
for(int i=x;i<=n;i=a[i].end)
ans+=a[i].d;
return ans;
}
int main()
{
int t,x,y;
scanf("%d",&n);
block=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].d=0;
belong[i]=i/block+1;
if(belong[i]!=belong[i-1]) l[belong[i]]=i,r[belong[i-1]]=i-1;
}
r[belong[n]]=n;
init();
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d",&t);
if(t==1)
{
scanf("%d",&x);
x++;
printf("%d\n",solve(x));
}
else
{
scanf("%d %d",&x,&y);
x++;
a[x].x=y;
work(x);
}
}
}
当然还有 L C T LCT LCT的正确做法。
显然这是一道模板题。
当跳过了 n n n时,不妨使得这个值为 n + 1 n+1 n+1。这样不仅节省了空间,也使得询问操作更加方便。在询问操作时,不妨使 n + 1 n+1 n+1作为树根,然后查询 x x x节点的重链大小即可。注意要减 1 1 1(减去自己)。
#include
#include
#include
using namespace std;
struct node{int fa,size,lazy,son[2];} tr[200010];
int match[200010];
int n,m;
bool isroot(int x)
{
return tr[tr[x].fa].son[0]!=x&&tr[tr[x].fa].son[1]!=x;
}
bool isson(int x)
{
return x==tr[tr[x].fa].son[1];
}
void change(int x)
{
if(!x)return;
swap(tr[x].son[0],tr[x].son[1]);
tr[x].lazy^=1;
}
void pushup(int x)
{
tr[x].size=1+tr[tr[x].son[0]].size+tr[tr[x].son[1]].size;
}
void pushdown(int x)
{
if(!tr[x].lazy) return;
change(tr[x].son[0]),change(tr[x].son[1]);
tr[x].lazy=0;
}
void rot(int x)
{
int w=isson(x),y=tr[x].fa,yy=tr[y].fa;
tr[y].son[w]=tr[x].son[w^1];
if(tr[y].son[w]) tr[tr[y].son[w]].fa=y;
tr[x].fa=yy;
if(!isroot(y)) tr[yy].son[isson(y)]=x;
tr[x].son[w^1]=y;tr[y].fa=x;
pushup(y);
}
int sta[300010];
int top;
void splay(int x)
{
sta[top=1]=x;
for(int i=x;!isroot(i);i=tr[i].fa)
sta[++top]=tr[i].fa;
while(top) pushdown(sta[top--]);
for(int y=tr[x].fa;!isroot(x);rot(x),y=tr[x].fa)
if(!isroot(y)) isson(x)^isson(y)?rot(x):rot(y);
pushup(x);
}
void access(int x)
{
for(int y=0;x;y=x,x=tr[x].fa)
splay(x),tr[x].son[1]=y,pushup(x);
}
int findroot(int x)
{
access(x);splay(x);
while(tr[x].son[0]) x=tr[x].son[0];
//splay(x);
return x;
}
void makeroot(int x)
{
access(x);splay(x);change(x);
}
void link(int x,int y)
{
makeroot(x);
if(findroot(y)!=x) tr[x].fa=y;
}
void del(int x,int y)
{
makeroot(x);
if(findroot(y)==x&&!tr[x].son[1]&&tr[x].fa==y) tr[x].fa=tr[y].son[0]=0,pushup(x),pushup(y);
}
int query(int x)
{
makeroot(n+1),access(x),splay(x);
return tr[x].size-1;
}
int main()
{
int t,x,y;
scanf("%d",&n);
for(int i=1;i<=n;i++)
tr[i]=(node){0,1,0,0,0};
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
match[i]=min(i+x,n+1);
link(i,match[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&t);
if(t==1)
{
scanf("%d",&x);
printf("%d\n",query(x+1));
}
else
{
scanf("%d %d",&x,&y);
del(x+1,match[x+1]);
match[x+1]=min(x+1+y,n+1);
link(x+1,match[x+1]);
}
}
}