背景:
计算复杂度为
Θ
(
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个位置。
思路1:
暴力分块。
记录两个东西:当前点跳出当前块的步数以及跳到的点。
可以逆向
d
p
dp
dp得出这两个东西。
于是就把正解为
L
C
T
LCT
LCT的省选题过了。
代码1:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
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);
}
}
}
思路2:
当然还有
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(减去自己)。
代码2:
#include<cstdio>
#include<cstring>
#include<algorithm>
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]);
}
}
}