[WC2023]楼梯 解题报告
在开始正文前,先分享一下笔者在 WC2023 的悲惨经历。
前 2.5h 在三题间反复横跳,啥都不会,心态爆炸,也根本没写什么暴力分;后 2.5h 猛然发现这题会做,没有想清楚代码细节就开始狂暴 rush,结果没冲出来。
于是悲惨打铜。
还不如先睡他 3.5h,然后起床花 1.5h 写三个暴力直接 Au 跑路!
经过两天,也差不多冷静下来了。希望自己能够知耻而后勇,在考场上享受比赛,做出正确的决策,心态稳健。
Solution
先考虑一个弱化的问题,怎么对一个静态的楼梯做 \(q\) 次查询操作。
我们可以用“楼梯序列”描述一个楼梯——每当遇到一个右边界,就在序列末尾加入一个 \(1\),每遇到一个下边界,就在序列末尾加入 \(0\)。
记这样的序列为 \(A\),序列的长度为 \(n\)。

考虑上图,有 \(A=[1,1,0,1,0,0,1,1,0],n=9\)。
容易发现这是个双射。
而楼梯序列中的子区间 \([l,r]\) 代表了原结构中的子楼梯。具体地,一个满足 \(A_l=1,A_r=0\) 的区间 \([l,r]\),和原结构以网格 \((\sum_{i=1}^{l} A_i,\sum_{j=r}^{n}1-A_j)\) 作为生成格产生的子楼梯是一个双射。
举几个例子,区间 \([2,3]\) 代表着以 \((2,4)\) 为生成格的子楼梯,区间 \([4,6]\) 代表了以 \((3,2)\) 为生成格的子楼梯。
在下文中,我们都将用子楼梯 \([l,r]\) 来描述这个区间映射到的子楼梯。
这样转换有什么好处呢?
一是,把一个二维空间上的问题压缩到了一维的序列上,方便用数据结构维护。
二是,注意到所谓的“边界格数”,实际上就是 \(\frac{X}{2}-1\),其中 \(X\) 代表楼梯的周长。对于子楼梯 \([l,r]\),这个值也就等于 \(r-l\),我们可以非常方便地算出边界格数。
考虑 ? q 怎么做,我们拎出 \(A_1,A_{1+q},A_{1+2q},\dots,A_{n-q},A_{n}\),记其为 \(B\),项数为 \(m\)。
如果 \(B\) 中存在相邻的 \(1,0\),我们就找到了一组解。
又因为 \(A_1=1,A_n=0\),我们可以断言,若 \(n>0\),一定有解。至于 \(n=0\) 的情况,则是无解。
考察 \(mid=\lfloor{\frac{1+m}{2}}\rfloor\),若有 \(B_{mid}=0\),则在 \([1,mid]\) 中必有一组解;反之,则在 \([mid,m]\) 中必有一组解。
这是一个可以二分的结构,查询就做完了。
初始我们先认为序列里有无穷多的 \(1\),这样更方便维护,这相当于把坐标轴也考虑了进来。当然,在查询的时候要去除多余的 \(1\)。
考虑剩下的操作怎么维护。
-
+ a b这相当于在第 \(a\) 个 \(1\) 后面加入 \(b\) 个 \(0\)。 -
- a b我们要去掉序列中后 \(b\) 个 \(0\),再在第 \(a\) 个 \(1\) 前加入 \(b\) 个 \(0\)。特别地,如果在第 \(a\) 个 \(1\) 之后的 \(0\) 的数量不足 \(b\),要另外处理一下。 -
R u由于保证前 \(u\) 个操作没有 \(R\),暴力还原或者栈序撤销显然是对的,但感觉直接可持久化会更好写。
以上所有操作都可以使用线段树维护——记录每个 \(1\) 与下一个 \(1\) 之间有多少个 \(0\)。
时间复杂度 \(O(nlog^2V)\)。在这里,\(V=10^9\)。
Code
p.s. 这份代码暂时没地方测试,不过大样例输出应该是对的。
#include<bits/stdc++.h>
#define IL inline
#define reg register
#define N 300300
#define M 10001000
#define int long long
IL int read()
{
reg int x=0; reg char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}
int n,len=1e9+5;
char s[10];
int rt[N],ls[M],rs[M],sum[M],tot;
IL int clone(reg int x){return ls[++tot]=ls[x],rs[tot]=rs[x],sum[tot]=sum[x],tot;}
void modify(reg int &o,reg int l,reg int r,reg int p,reg int k)
{
sum[o=clone(o)]+=k;
if(l==r)return; reg int mid=l+r>>1;
if(mid>=p)modify(ls[o],l,mid,p,k); else modify(rs[o],mid+1,r,p,k);
}
int suf_sum(reg int o,reg int l,reg int r,reg int p)
{
if(!o||p>r)return 0;
if(p<=l)return sum[o]; reg int mid=l+r>>1;
return suf_sum(ls[o],l,mid,p)+suf_sum(rs[o],mid+1,r,p);
}
int dfs(reg int o,reg int l,reg int r,reg int k)
{
if(l==r)return l; reg int mid=l+r>>1;
if(sum[rs[o]]>=k)return dfs(rs[o],mid+1,r,k);
return dfs(ls[o],l,mid,k-sum[rs[o]]);
}
void clear(reg int &o,reg int l,reg int r,reg int p)
{
if(p>r||!o)return;
if(p<=l)return o=0,void(); reg int mid=l+r>>1;
o=clone(o),clear(ls[o],l,mid,p),clear(rs[o],mid+1,r,p);
sum[o]=sum[ls[o]]+sum[rs[o]];
}
int query(reg int o,reg int l,reg int r,reg int k)
{
if(l==r)return k==1?l:-l; reg int mid=l+r>>1;
if(mid-l+1+sum[ls[o]]>=k)return query(ls[o],l,mid,k);
return query(rs[o],mid+1,r,k-(mid-l+1+sum[ls[o]]));
}
void dc(reg int l,reg int r,reg int d,reg int rt)
{
if(l==r-1)
{
reg int p=query(rt,1,len,l*d+1),q=-query(rt,1,len,r*d+1);
return printf("%lld %lld\n",p,suf_sum(rt,1,len,p)-(d-q+p)+1),void();
}
reg int mid=l+r>>1;
if(query(rt,1,len,mid*d+1)>0)dc(mid,r,d,rt); else dc(l,mid,d,rt);
}
main()
{
n=read();
for(reg int a,b,p,k,i=1;i<=n;++i)
{
scanf("%s",s),a=read(),rt[i]=rt[i-1];
if(s[0]=='+')modify(rt[i],1,len,a,read());
else if(s[0]=='-')
{
b=read();
if(sum[rt[i]]<=b&&a==1){rt[i]=0; continue;}
p=dfs(rt[i],1,len,b);
if(p>=a)
{
modify(rt[i],1,len,p,suf_sum(rt[i],1,len,p+1)-b),clear(rt[i],1,len,p+1);
if(a>1)modify(rt[i],1,len,a-1,b);
}else
{
if(a>1)modify(rt[i],1,len,a-1,suf_sum(rt[i],1,len,a));
clear(rt[i],1,len,a);
}
}else if(s[0]=='R')rt[i]=rt[i-a-1];
else
{
p=dfs(rt[i],1,len,1),k=p-1+sum[rt[i]];
if(!k){puts("-1 -1"); continue;}
dc(0,k/a,a,rt[i]);
}
}
}

浙公网安备 33010602011771号