luogu P3203 [HNOI2010]弹飞绵羊

背景:

计算复杂度为 Θ ( 2 n n ) \Theta(2*n\sqrt{n}) 的代码被卡到了 30 30 分(大概是用了 S T L STL 吧)。在 b r z brz 的提醒下写了一个逆向 d p dp ,复杂度为 Θ ( n n ) \Theta(n\sqrt{n}) ,竟然神奇地过了。
后来又写了一个 L C T LCT ,跑得好像差不多。

题目传送门:

https://www.luogu.org/problemnew/show/P3203

题意:

n n 个位置,每个位置有一个弹跳系数(正整数),支持修改,求当前询问的点多少次能跳出这 n n 个位置。

思路1:

暴力分块。
记录两个东西:当前点跳出当前块的步数以及跳到的点。
可以逆向 d p dp 得出这两个东西。
于是就把正解为 L C T 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 的正确做法。
显然这是一道模板题。
当跳过了 n n 时,不妨使得这个值为 n + 1 n+1 。这样不仅节省了空间,也使得询问操作更加方便。在询问操作时,不妨使 n + 1 n+1 作为树根,然后查询 x x 节点的子树大小即可。注意要减 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]);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/zsyz_ZZY/article/details/85223103