Maximum Rating
- 通过这道题,对Splay有了更深刻的理解
- Splay的中序遍历代表当前序列,通过查询排名为i的点找到序列中的第i个数,这些信息是完全由Splay的结构提供的
- 通过Splay的编号,我们则可以访问到原序列对应的节点
- 其实这道题完全是可以用线段树做的,既然普通线段树不行,那不妨用权值线段树实现【顺序自动机】
- 数据结构的程序很难通过一般的输出中间结果法调试,因为问题出现的时候往往不是问题产生的时候
- 你常告诫自己“想清楚了再写代码“,可是这道题的思路难道不清楚吗?是的,思路很清楚,重要的是你该如何实现它呢?
- 尝试了一种新的Splay树写法,即忽视相同的权值,使得节点的标号也能存储一定的信息;但这样做的代价是,你没办法直接通过两遍Splay轻松地定位到要删除的节点,这是你当初纠结的点之一
是一道典型的 转换题意->选择数据结构->码 的题目,也是“熟悉程序语法”到“合格竞赛选手”的门槛。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,h[200005],root;
struct t1
{
int fa,s[2];
int va,size;
long long sum;
t1()
{
fa=s[0]=s[1]=0;
va=size=sum=0;
}
}t[200005];
bool get(int x)
{
return x==t[t[x].fa].s[1];
}
void maintain(int x)
{
t[x].sum=t[t[x].s[0]].sum+t[t[x].s[1]].sum+t[x].va;
t[x].size=t[t[x].s[0]].size+t[t[x].s[1]].size+1;
}
void rotate(int x)
{
int y=t[x].fa,z=t[y].fa,opt=get(x);
t[y].s[opt]=t[x].s[opt^1];
if(t[x].s[opt^1])
{
t[t[x].s[opt^1]].fa=y;
}
t[x].s[opt^1]=y;
t[y].fa=x;
t[x].fa=z;
if(z)
{
t[z].s[y==t[z].s[1]]=x;
}
maintain(y);
maintain(x);
}
void update(int x)
{
maintain(x);
if(t[x].fa)
{
update(t[x].fa);
}
}
void Splay(int x,int k)
{
update(x);
while(t[x].fa!=k)
{
int y=t[x].fa,z=t[y].fa;
if(z!=k)
{
if(get(x)==get(y))
{
rotate(y);
}
else
{
rotate(x);
}
}
rotate(x);
}
if(!k)
{
root=x;
}
}
void output(int p)
{
if(!p)
{
return;
}
output(t[p].s[0]);
cout<<" "<<t[p].va;
output(t[p].s[1]);
}
int rnk(int p,int va)
{
if(t[t[p].s[0]].size==va-1)
{
return p;
}
else if(t[t[p].s[0]].size<va-1)
{
return rnk(t[p].s[1],va-t[t[p].s[0]].size-1);
}
else
{
return rnk(t[p].s[0],va);
}
}
long long ask(int p)
{
Splay(rnk(root,1),0);
Splay(rnk(root,p+2),rnk(root,1));
return t[t[rnk(root,p+2)].s[0]].sum;
}
void insert(int x)
{
int p=root;
while(p)
{
if(t[x].va>t[p].va)
{
if(!t[p].s[1])
{
t[p].s[1]=x;
t[x].fa=p;
Splay(x,0);
return;
}
p=t[p].s[1];
}
else
{
if(!t[p].s[0])
{
t[p].s[0]=x;
t[x].fa=p;
Splay(x,0);
return;
}
p=t[p].s[0];
}
}
}
int pre()
{
int cur=t[root].s[0];
while(t[cur].s[1])
{
cur=t[cur].s[1];
}
Splay(cur,0);
return cur;
}
int merge(int x,int y)
{
if(!x)
{
return y;
}
if(!y)
{
return x;
}
int p=pre();
t[p].s[1]=y;
t[y].fa=p;
maintain(p);
return p;
}
void clear(int x)
{
t[x].fa=t[x].s[0]=t[x].s[1]=t[x].sum=t[x].size=0;
}
void erase(int x)
{
Splay(x,0);
t[t[x].s[0]].fa=t[t[x].s[1]].fa=0;
root=merge(t[x].s[0],t[x].s[1]);
clear(x);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int q,maxn=0;
cin>>n>>q;
t[1].va=INT_MIN;
t[n+2].va=INT_MAX;
t[1].s[1]=n+2;
t[n+2].fa=1;
maintain(n+2);
maintain(1);
root=1;
h[1]=1;
for(int i=2;i<=n+1;i++)
{
cin>>t[i].va;
insert(i);
h[i]=i;
maxn+=(t[i].va>0);
}
auto check=[](int p)
{
return ask(p)<=0;
};
for(int i=1;i<=q;i++)
{
int x,v;
cin>>x>>v;
maxn-=(t[x+1].va>0);
maxn+=(v>0);
t[x+1].va=v;
erase(x+1);
insert(x+1);
int p=partition_point(h+1,h+n+1,check)-h;
int minn=n+1-p;
cout<<maxn-minn+1<<"\n";
}
return 0;
}