YACS 2022年11月月赛 乙组 T1 数对统计 题解

题目链接

好久没更了,闲话一句,我的初赛成绩只有 $71.5$,$76$ 才能进 $NOIP$,所以就更一篇吧

首先先考虑暴力算法,枚举两个数,设这两个数为 $x$ 和 $y$,

如果 $f[x][y]=false$,那就标记为 $true$,$ans ++$

这种方法可以优化,怎么优化呢?

假如一个序列里出现了好几个 $x$,但是和 $x$ 能构成序对的只有两种情况:

$x$ 在左边,另一个在右边

$x$ 在右边,另一个在左边

这样我们仅需要把 $x$ 出现的第一次,最后一次记录下来,

然后统计最右边的 $x$ 左边可以构成多少,最左边的 $x$ 右边可以构成多少

可以构成多少就是有多少不重复的,数字范围很小,开桶即可

容易证明,这两个构造的数对最多只有一个一样,即 $xx$ 和 $xx$,

如果 $x$ 出现的次数大于 $1$ 次那么就会产生这个重复,

减一即可,然后除以二($xy$ 和 $yx$ 重算)

再优化,想到我们只统计 $x$ 最后一次出现的左边有多少不一样的即可。

或者第一次出现的右边,这里取最后一次出现的左边(比较方便)

先证明为啥是对的,这样肯定能统计到 $yx$,那么 $xy$ 呢?

答:会在 $y$ 的回合被统计

离线乱搞,把区间排序之后再算,离线 $O(n)+$ 排序 $O(n log n)$ 搞定

代码:

#include <iostream>
#include <algorithm>
using namespace std;
int n, t, ri;
int a[100005], b[100005];
int r[100005], q[100005];
long long ans, pre;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++)
    {
        cin >> a[i];
        r[a[i] ] = i;
    }
    for (int i = 1; i <= 100000; i ++) if (r[i] != 0) q[++ t] = r[i] - 1;
    sort (q + 1, q + t + 1);
    for (int i = 1; i <= t; i ++)
    {
        while (ri < q[i])
        {
            ri ++;
            if (b[a[ri] ] == 0) pre ++;
            b[a[ri] ] ++;
        }
        ans += pre;
    }
    cout << ans << endl;
    return 0;
}
posted @ 2022-11-22 18:49  Xy_top  阅读(175)  评论(6)    收藏  举报