题解 [JROI R6] 射命丸文的取材之旅
欸你谷的月赛质量啥时候这么高了(
只会到 \(O(n^2)\),然而没有这档分
估计出题人也想不到有我这样的 nt 选手吧
先说 \(O(n^2)\):
这题看着挺扫描线的,那就尝试扫描线维护 \(\rm mex\)
那么枚举 \(r\),尝试不断减小 \(l\)
强制 \(a_i<b_i\),每个位置都贪心选 \(b_i\)
发现位置 \(i\) 选数的时候有几种可能:
- 选 \(b_i\) 时 \(\rm mex\) 不变:那就选 \(b_i\)
- 选 \(b_i\) 会使 \(\rm mex\) 变大:那就选 \(a_i\)
若 \(a_i=b_i\) 那么 \(\rm mex\) 必须变大到 \(\rm mex+1\)
然后有可能 \(j>i\) 已经选过 \(\rm mex+1\) 这个数了,那么它要继续变大
考虑 \(\forall j>i\),在 \(j\) 处选了 \(b_j\) 且 \(b+j=\rm mex+1\)
那么将其改为选 \(a_j\)
若 \(\rm mex\) 还是要变大那就递归变
这样的话每个 \(i\) 最多从选 \(b_i\) 变为选 \(a_i\) 一次
\(\rm mex\) 的上界是 \(n\),最多变大 \(n\) 次
所以单次均摊复杂度 \(O(n)\)
算上枚举 \(r\),复杂度 \(O(n^2)\)
正确性容易归纳证明
然后正解:
\(\tt key\ observation\) 是只有 \(a_i=b_i\) 的位置 \(i\) 是有用的
剩下的位置我们根本不关心它们的 \(a, b\) 具体是什么
证明:
考虑将所有 \(a_i=b_i\) 的 \(a_i\) 填入到值域当中
考虑此时的 \(\rm mex\),因为剩下的都满足 \(a_i\neq b_i\) 所以若两个都不等于当前 \(\rm mex\) 那就随便选,否则选 \(\neq \rm mex\) 的那个
然后就好做了:只考虑 \(a_i=b_i\) 的位置,对权值开桶(特别加入 \(0\) 和 \(n+1\))
扫描桶中的每个位置 \(i\),令 \(lst_i\) 为上一个权值为当前权值的位置
那么 \(\forall l, r\in(lst_i, i)\) 都有 \(\rm mex(l, r)\leqslant\) 当前权值,用这个更新答案就好了
复杂度 \(O(n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
vector<int> sta[N];
int a[N], b[N], c[N], ans;
signed main()
{
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
for (int i=1; i<=n; ++i) b[i]=read();
for (int i=0; i<=n; ++i) sta[i].pb(0);
for (int i=1; i<=n; ++i)
if (a[i]==b[i]) sta[c[i]=a[i]].pb(i);
else sta[c[i]=n+1].pb(i);
for (int i=0; i<=n; ++i) {
sta[i].pb(n+1);
for (int j=1; j<sta[i].size(); ++j) {
int l=sta[i][j-1]+1, r=sta[i][j]-1;
if (l<=r) ans=max(ans, r-l+1-i);
}
}
printf("%d\n", ans);
return 0;
}