P8413 「SvR-1」Five of Pentacles 题解
UPD @ 2022-10-05 :修改了一些误导性的内容
赛时的时候在期末复习,就没做。
期末考完来看的时候我的内心 OS 如下:
这不是求个最大不下降子序列就好了吗?这么简单也能评蓝?
然后看到
n,m,k\le 2\times10^6 的时候我凝固了。
好毒瘤一题
以上是我当时不知道 BIT 常数有多小的时候的**言论,大可以忽略。
当然我这个利用在线格式的奇怪做法还是挺有意思的(?
(最终复杂度是与正解差不多的)
正文
我们来看一下题目啊(传送门)
题目要求最小的越过障碍数,那么我们很容能够想到两点:
- 如果没有障碍消失,那么最终越过障碍数就是
n+m-1 。 - 我们每走过一个消失的障碍,最终越过的障碍数就减少
1 。
结合这两点我们希望越过尽可能多的消失的障碍。
而越过的障碍,我们用题目中的
不难发现根据
但是我们就是要走不寻常的路。我们看在线输入的格式。
接下来
k 行,其中第i 行有两个数字t_i, p 。其中p 用于生成x_i ,即:x_i = \min(x_{i - 1} + p \oplus (lastans \bmod 15) + 1, m) 其中lastans 表示上次变化的答案。特别地,第一次变化之前
lastans = 0 ,x_0 = 0 ,且当x_{i - 1} = m 时,将x_{i - 1} 视作0 (注意这不会真的改变x_{i - 1} 的值)。
同时我们有
那么我们会发现我们得到的
利用这个“严格单调增”的性质,我们可以用数组
记最大跨过的消失的障碍数为
按照
那么我们发现当
处理完之后我们再用下面的 DP 柿子倒序更新
然后就做完了。主要的复杂度在更新一整个
注意一下在线格式的细节就好了,怕 TLE 就用上快读。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define MAXN 2000000
using namespace std;
//快读
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline void read(T& r) {
r=0;bool w=0; char ch=getchar();
while(ch<'0'||ch>'9') w=ch=='-'?1:0,ch=getchar();
while(ch>='0'&&ch<='9') r=r*10+(ch^48), ch=getchar();
r=w?-r:r;
}
int a[MAXN+5],tot,stk[MAXN+5],x[MAXN+5],now;
//stk是t时间下的p序列,x是解密以后的x序列
int main() {
ios::sync_with_stdio(0);
int n,m,k,t,ans=0,p,lst=0,lstans=0;
read(n);read(m);read(k);
read(t);read(p);
stk[++tot]=p;
now=t;
for (int i=1;i<=k;++i) {
if (i==k) t=p=0;
else read(t),read(p);
if (t==now) {
stk[++tot]=p;
} else {
//依次更新并输出答案
for (int j=1;j<=tot;++j) {
lst=min(lst+(stk[j]^(lstans%15))+1,m);
x[j]=lst;
ans=max(ans,a[lst]+j);
cout<<n+m-ans-1<<"\n";
lstans=n+m-ans-1;
}
//更新a数组
++a[x[tot]];
for (int j=tot-1;j>=1;--j) a[x[j]]=max(a[x[j+1]],a[x[j]])+1;
//在处理完后把下一个p放入stk
stk[tot=1]=p;
now=t;
if (lst==m) {
for (int j=m-1;j>=1;--j) a[j]=max(a[j+1],a[j]);
lst=0;
}
}
}
return 0;
}