题解:P9310 [EGOI2021] Luna likes Love / 卢娜爱磕 cp

· · 题解

P9310题解

题目大意

给定一个长度为 2n 的正整数序列,保证所有 1\le i\le n 恰好出现两次,有以下两种操作,求清空序列的最小操作数:

综上,顺序不交换比交换更优或一样。一般地,操作顺序应按情侣间隔人数多少升序排列。

证毕。

AC Code

#include <bits/stdc++.h>
#define int long long //记得开long long 
#define lowbit(x) ((x) & -(x))
using namespace std;
const int N = 5e5, A = N << 1;
int n, a[A + 5], tr[A + 5], ans;
struct CP{
    int n, f, s;
} cp[N + 5];

bool cmp(CP a, CP b){
    return a.s - a.f < b.s - b.f;
}

void mns(int x){ for(; x <= (n << 1); x += lowbit(x)) -- tr[x]; }

int get(int x){
    int res = 0;
    for(; x; x -= lowbit(x)) res += tr[x];
    return res;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    ans = n;
    for(register int i = 1; i <= (n << 1); ++ i){
        cin >> a[i];
        (cp[a[i]].f ? cp[a[i]].s : cp[a[i]].f) = i;
        tr[i] = lowbit(i); // 树状数组全部置1 
    }
    for(register int i = 1; i <= n; ++ i) cp[i].n = i;
    sort(cp + 1, cp + n + 1, cmp);
    for(register int i = 1; i <= n; ++ i){
        ans += get(cp[i].s - 1) - get(cp[i].f);
        mns(cp[i].f);
        mns(cp[i].s);
    }
    cout << ans;
    return 0;
}

解法 B

思路

准备好一个“栈”。
遍历 a 数组,如果该元素不在栈中(第一次出现),则入栈;否则(第二次出现),将答案加上栈内该元素到栈顶的距离,并弹出栈内该元素。
考虑到一般的栈无法实现任意查询与弹出,使用树状数组模拟“栈”。

时间复杂度

对于 a 数组中的每一个元素,都要使用树状数组进行查改,故时间复杂度为 O(n\log n)

AC Code

考试代码,没开 long long 拿了 84

#include <bits/stdc++.h>
#define lowbit(x) ((x) & -(x))
using namespace std;
int n, a[1000006], tim[500005], tot, tr[500005];

void add(int x){
    tim[x] = ++ tot;
    for(register int i = tot; i <= n; i += lowbit(i)) ++ tr[i];
}

void del(int x){
    for(register int i = tim[x]; i <= n; i += lowbit(i)) -- tr[i];
    tim[x] = 0;
}

int get(int x){
    int res = 0;
    for(; x; x -= lowbit(x)) res += tr[x];
    return res;
}

void solve(){ //一开始想暴力骗分,题解中删掉了
    long long ans = n; // 只需要这里开long long
    for(register int i = 1; i <= (n << 1); ++ i){
        if(tim[a[i]]){
            ans += get(tot) - get(tim[a[i]]);
            del(a[i]);
        }
        else add(a[i]);
    }
    cout << ans;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for(register int i = 1; i <= (n << 1); ++ i) cin >> a[i];
    solve();
    return 0;
}

注意事项

long long