题解:CF773E Blog Post Rating
前几天某同学翻到此题的一个神秘的最短解,码长非常短,且时间复杂度线性。研究了一会研究明白了,然后发现洛谷题解区居然没有这个做法,遂来写题解。
Part 1. 并查集去重
第一步和之前的题解一样:
使用并查集将每个输入的
具体地,令并查集
因为重复的
可是,即使是带上路径压缩加上启发式合并的并查集也是
事实上,利用此题并查集的特殊结构,只需要普通路径压缩即可做到严格线性。用均摊分析思想,因为每一次这样的操作都会消灭一整条链(由于路径压缩),则每条边
Part 2. 考虑评分变化图象
所有
- 从
0 开始; - 先有一个下降段(每次严格
-1 ); - 再一个不变段(该段长度
\le1 ,否则就必有重复的数,与x 两两不同矛盾); - 最后一个上升段(每次严格
+1 ,这是由两两不同的性质保证的,不会出现其他做法中+1 和不变交替的情况);
注意这三个段中任意一个的长度都有可能为
相当于只有两种图象,一个是 V 形,另一个是 V 形的两侧之间加一条平着的线。
有了这个性质,图象就很好刻画了。我们令
不难发现
易知任何时刻都有
Part 3. 分讨加入一个新数 x 对结构的影响
x>p
则此时
x=p
当前的数就是最小值,则这个数会加入不变段,
x<p
该数显然会加入下降或者不变段,所以先
但是
\small{r+p\ge2}
此时不变段长度
不难发现这会导致下降段长度
\small{r+p=1}
则不变段长度为
我们考虑这一个
- 没出现过
p 这个数,则最低点还没被摁死,可以“局部微调”,继续下降,相当于将整个后面的结构平稳地下移一格,下降段+1 ,不变段-1 ,p\gets p-1 ; - 否则,想再让一个数落在
p ,就违反了高度唯一的性质。我们无法新增一次合法的-1 ,就不能继续下降了,这里积累的压力只能等到以后“整体重构”,套用r+p\ge2 的做法强行使之下降。Part 4. Code
通过以上讨论,我们可以得到如下十分简洁的代码: :::success[AC Code] AC 记录。
#include <bits/stdc++.h>
#define rep1(i,x,y) for (int i = (x);i <= (y);i++)
using namespace std;
const int N = 2e6 + 10;
int n,p,r,x;
int f[N];
bool v[N];
int* fa = f + N / 2;
bool* vis = v + N / 2;
int find(int x)
{
if (fa[x] == x)
return x;
return fa[x] = find(fa[x]);
}
signed main()
{
rep1(i,-1e6,1e6)
fa[i] = i;
cin >> n;
rep1(i,1,n)
{
cin >> x;
x = find(x);
vis[x] = true;
fa[x] = fa[x - 1];
if (x == p)
r++;
else if (x < p)
{
r++;
if (p + r == 1)
{
if (!vis[p])
p--;
}
else
{
p--;
r--;
}
}
cout << p + i - r << '\n';
}
}
:::