弹飞绵羊
作者:互联网
弹飞绵阳,洛谷。花了2000ms,o2后1000ms
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define R register int
#define I inline void
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N=1e6;
inline int in()
{
int s=0;
char ch = getchar();
while(ch < '0' || ch > '9')ch = getchar();
while(ch >= '0' && ch <= '9')s = (s<<1) + (s<<3) + (ch^48),ch = getchar();
return s;
}
int f[N],c[N][2],v[N],s[N],st[N],go[N],va[N];
bool r[N];
inline bool nroot(R x) //判断节点是否为一个Splay的根(与普通Splay的区别1)
{
return c[f[x]][0]==x||c[f[x]][1]==x;
}//原理很简单,如果连的是轻边,他的父亲的儿子里没有它
I pushup(R x) //上传信息
{
s[x]=s[lc]+s[rc]+v[x];
}
I pushr(R x)
{
R t=lc; //翻转操作
lc=rc;
rc=t;
r[x]^=1;
}
I pushdown(R x) //判断并释放懒标记
{
if(r[x])
{
if(lc)
pushr(lc);
if(rc)
pushr(rc);
r[x]=0;
}
}
I rotate(R x) //一次旋转
{
R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
if(nroot(y))
c[z][c[z][1]==y]=x;
c[x][!k]=y;
c[y][k]=w;//额外注意if(nroot(y))语句,此处不判断会引起致命错误(与普通Splay的区别2)
if(w)
f[w]=y;
f[y]=x;
f[x]=z;
pushup(y);
}
I splay(R x) //只传了一个参数,因为所有操作的目标都是该Splay的根(与普通Splay的区别3)
{
R y=x,z=0;
st[++z]=y;//st为栈,暂存当前点到根的整条路径,pushdown时一定要从上往下放标记(与普通Splay的区别4)
while(nroot(y))
st[++z]=y=f[y];
while(z)
pushdown(st[z--]);
while(nroot(x))
{
y=f[x];
z=f[y];
if(nroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?x:y);
rotate(x);
}
pushup(x);
}
/*当然了,其实利用函数堆栈也很方便,代替上面的手工栈,就像这样
I pushall(R x){
if(nroot(x))pushall(f[x]);
pushdown(x);
}*/
I access(R x) //访问
{
for(R y=0; x; x=f[y=x])
splay(x),rc=y,pushup(x);
}
I makeroot(R x) //换根
{
access(x);
splay(x);
pushr(x);
}
int findroot(R x) //找根(在真实的树中的)
{
access(x);
splay(x);
while(lc)
pushdown(x),x=lc;
splay(x);
return x;
}
I split(R x,R y) //提取路径
{
makeroot(x);
access(y);
splay(y);
}
I link(R x,R y) //连边
{
makeroot(x);
if(findroot(y)!=x)
f[x]=y;
}
I cut(R x,R y) //断边
{
makeroot(x);
if(findroot(y)==x&&f[y]==x&&!c[y][0])
{
f[y]=c[x][1]=0;
pushup(x);
}
}
int main()
{
R n=in();
for(R i=1; i<=n; ++i)
{
va[i] = in();//系数
go[i] = (i+va[i]<=n)?(i+va[i]):n+1;
link(i,go[i]);
v[i] = 1;
}
R m = in();
while(m--)
{
R type=in(),x=in();
x++;
switch(type)
{
case 1:
split(x,n+1);
printf("%d\n",s[n+1]);
break;
case 2:
R y = in();
cut(x,go[x]);
splay(x);//先把x转上去再改,不然会影响Splay信息的正确性
va[x]=y;
go[x] = (x+va[x]<=n)?(x+va[x]):n+1;
link(x,go[x]);
}
}
return 0;
}
https://www.cnblogs.com/flashhu/p/8349984.html
这个链接是flashu大佬的,他说修改边的时候,根本不需要makeroot(x)
因为 我们并不需要查询或者更改指定路径(x−y)(x−y)的信息。
我们只是连边删边,并不改变这条边的信息,所以不需要makeroot
嗯,又学到一个
int main()
{
register char ch;
R n,m,j,k;
in(n);
for(j=1;j<=n;++j){
s[j]=1;
in(k);
if(j+k<=n)f[j]=j+k;//如果弹飞了就不连边
}
in(m);
while(m--){
gc;
if(ch&1){
in(j);++j;
access(j);splay(j);//直接查询
printf("%d\n",s[j]);
}
else{
in(j);in(k);++j;
access(j);splay(j);
c[j][0]=f[c[j][0]]=0;//直接断边
if(j+k<=n)f[j]=j+k;//直接连边
pushup(j);
}
}
return 0;
}
标签:ch,int,弹飞,char,绵羊,getchar,define 来源: https://blog.csdn.net/qq_43439240/article/details/94359709