【HNOI 2010】弹飞绵羊

【题目】

传送门

题目描述:

Lostmonkey 发明了一种超级反弹装置。为了在绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey 在地上沿一条直线摆放 n n n 个反弹装置,并按从前往后的方式将反弹装置依次编号为 0 0 0 n − 1 n-1 n1,对 0 ≤ i ≤ n − 1 0≤i≤n-1 0in1,为第 i i i 个反弹装置设定了初始弹力系数 k i k_i ki,当绵羊落到第 i i i 个反弹装置上时,它将被往后弹出 k i k_i ki 步,即落到第 i + k i i+k_i i+ki 个反弹装置上,若不存在第 i + k i i+k_i i+ki 个反弹装置,则绵羊被弹飞。绵羊想知道:从第 i i i 个反弹装置开始,它被弹出几次(含被弹飞的那次)后会被弹飞。为使游戏更有趣,Lostmonkey 还可以修改某个反弹装置的弹力系数,但任何时候弹力系数均为正整数。

输入格式:

输入第一行是一个整数 n n n ,表示地上摆放 n n n 个反弹装置。

输入第二行是用空格隔开的 n n n 个正整数 k 0 k_0 k0 k 1 k_1 k1 ⋯ ⋯ k n − 1 k_{n-1} kn1,分别表示 n n n 个反弹装置的初始弹力系数。

输入第三行是一个正整数 m m m,表示后面还有 m m m 行输入数据。

接下来的 m m m 行,每行至少有用空格隔开的两个整数 i i i j j j ,若 i = 1 i=1 i=1,则你要输出从第 j j j 个反弹装置开始,被弹出几次后会被弹飞;若 i = 2 i=2 i=2,则该行有用空格隔开的三个整数 i i i j j j k k k,表示第 j j j 个反弹装置的弹力系数被修改为 k k k

输出格式:

输出包含的行数等于输入文件最后 m m m 行中 i = 1 i=1 i=1 的行数。第 h h h 行输出一个整数,表示对输入中给出的第 h h h 个求弹出次数的问题,基于 n n n 个反弹装置当时的弹力系数,求出的弹出次数。

样例数据:

输入
4
1 2 1 1
3
1 1
2 1 1
1 1

输出
2
3

备注:

【数据范围】

20 % 20\% 20% 的数据满足: n , m ≤ 10000 n,m≤10000 n,m10000
100 % 100\% 100% 的数据满足: n ≤ 200000 n≤200000 n200000 m ≤ 100000 m≤100000 m100000


【分析】

LCT 的模板题。

为了方便,我把下标编号加 1 1 1,即将下标编号变为 1 1 1 n n n

对于 i i i,如果 i + k i ≤ n i+k_i\le n i+kin,就连一条 ( i , i + k i ) (i,i+k_i) (i,i+ki) 的边,表示从 i i i 可以弹到 i + k i i+k_i i+ki;否则就连 ( i , n + 1 ) (i,n+1) (i,n+1) 的边,表示从 i i i 可以弹出去。

这样连出来后必然是一棵树,因为虽然每个点可以连进来很多条,但只能连出去一条。

修改操作很简单,先 Cut 掉原来连的边,再连上以当前弹力系数对应的边就行了。

然后是询问操作,我们先把 n + 1 n+1 n+1 变成这棵树的跟(Makeroot 操作),然后我们把 x x x 到根的路径提取出来(Access 操作),再把 x x x 转到辅助树的根(Splay 操作),那么最后 s i z e x − 1 size_x-1 sizex1 就是答案了。

应该还是比较好懂的吧。。


【代码】

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
int n,m,a[N],fa[N],son[N][2],mark[N],Size[N];
stack<int>stk;
int Get(int x)
{
	return x==son[fa[x]][1];
}
bool Isroot(int x)
{
	if(!fa[x])  return true;
	return (son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x);
}
void Pushup(int x)
{
	Size[x]=Size[son[x][0]]+Size[son[x][1]]+1;
}
void Pushdown(int x)
{
	if(!mark[x])  return;
	if(son[x][0])  mark[son[x][0]]^=1;
	if(son[x][1])  mark[son[x][1]]^=1;
	swap(son[x][0],son[x][1]),mark[x]=0;
}
void Rotate(int x)
{
	int y=fa[x],z=fa[y];
	int k=Get(x),l=son[x][k^1];
	son[y][k]=l;fa[l]=(l?y:0);
	if(!Isroot(y))  son[z][Get(y)]=x;fa[x]=z;
	son[x][k^1]=y,fa[y]=x;
	Pushup(y),Pushup(x);
}
void Splay(int x)
{
	stk.push(x);
	for(int i=x;!Isroot(i);i=fa[i])  stk.push(fa[i]);
	while(!stk.empty())  Pushdown(stk.top()),stk.pop();
	while(!Isroot(x))
	{
		int y=fa[x];
		if(!Isroot(y))  Rotate(Get(x)==Get(y)?y:x);
		Rotate(x);
	}
}
void Access(int x)
{
	int i;
	for(i=0;x;x=fa[i=x])
	  Splay(x),son[x][1]=i,Pushup(x);
}
void Makeroot(int x)
{
	Access(x);
	Splay(x);
	mark[x]^=1;
}
void Link(int x,int y)
{
	Makeroot(x);
	fa[x]=y;
}
void Cut(int x,int y)
{
	Makeroot(x);
	Access(y);Splay(y);
	son[y][0]=fa[x]=0;
	Pushup(y);
}
int Query(int x)
{
	Makeroot(n+1);
	Access(x),Splay(x);
	return Size[x]-1;
}
void Modify(int x,int y)
{
	Cut(x,(x+a[x]<=n)?x+a[x]:n+1);
	Link(x,(x+y<=n)?x+y:n+1),a[x]=y;
}
int main()
{
	int x,y,i,op;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d",&a[i]);
		Link(i,(i+a[i]<=n)?i+a[i]:n+1);
	}
	scanf("%d",&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&op,&x);
		if(op==1)  printf("%d\n",Query(++x));
		if(op==2)  scanf("%d",&y),Modify(++x,y);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值