cf_719_2D题解

81 阅读3分钟

题目描述

循环数组 AA ,长度为 n(1n105)n(1≤n≤10^5) ,若 a[i%n]a[i\%n]a[(i+1)%n]a[(i+1)\%n] 互质,则删去 a[(i+1)%n]a[(i+1)\%n] ,从循环数组第一个元素开始遍历,对于所有 a[i%n]a[i\%n] ,每次只能删去后面的一个元素,遍历数组直到没有元素可删,求删除元素的顺序

示例

输入:用例数 tttt 组数组长度 nn 和数组元素 a1,a2,...ana1,a2,...an ,其中 1ai1091≤ai≤10^9
5
5
5 9 2 10 15
6
1 2 4 2 4 2
2
1 2
1
1
1
2
输出:被删除元素个数,以及它们的的索引 i1<=i<=ni(1 <= i <= n),按删除顺序排列
2 2 3 
2 2 1 
2 2 1 
1 1 
0 

分析

一开始我的解法就是按照我所解析的题干那样,建立一个循环链表去循环遍历,每次记录本次循环中被删除元素的个数,直到这个个数为0,当时将题目理解到这一步就理所当然地这么做了,完全没有想过这样做的复杂度有多么恐怖,于是理所当然的超时了。所以做题前一定要看数据范围,根据数据范围选择相应复杂度的算法,这一点非常重要!(又忘了,气得想扇自己两耳光),达到 10510^5 量级的题的复杂度最高 O(nlogn)O(nlogn)

这一题的核心是删除元素,如果不实际删除元素,而只是用标志位标记,那么每次都要在已经删除的元素中耗费大量的判断时间,所以必须用删除代价小的数据结构来存储元素,显然就是链表。一开始我是自己写了一个单向循环链表,后来参考别人的代码,知道了 STLSTLListList 底层实现就是双向循环链表,所以可以直接拿过来用。但是用了List需要注意到删除元素后元素索引会有所变动,所以一定在事先保存所有元素的索引。

然后,可以用一个队列,保存所有可能的元素对{a, b},满足 gcd(a, b) == 1,可以将其记为类型A。我们事先将所有相邻的元素对都加入队列中,之后每次将队首弹出直至队列为空,当且仅当队首的两个元素还在数组中,且构成A类元素对时,我们将队首第二个元素从数组中删除,将其索引加入结果数组中,且寻找b在数组中的下一元素c,将{a, c}添加到队尾。这样做的复杂度是 O(nlogC)O(nlogC)

代码

#include <bits/stdc++.h>

using namespace std;

inline int gcd(int a,int b) {
    int r;
    while(b > 0) {
        r = a%b;
        a = b;
        b = r;
    }
    return a;
}

void solve(list<pair<int,int>>& L, int n) {
    queue<pair<int, int>> Q;
    for(int i = 0; i < n-1; i++) {
        Q.emplace(i, i+1);
    }
    Q.emplace(n-1, 0);

    typedef list<pair<int,int>>::iterator node;
    vector<node> M(n);
    int idx = 0;
    for(auto p = L.begin(); p != L.end(); p++) {
        M[idx++] = p;
    }

    vector<int> removed;
    while(!Q.empty()) {
        int a = Q.front().first;
        int b = Q.front().second;
        Q.pop();

        if(M[a] == L.end() || M[b] == L.end()) continue;

        int g = M[a]->second, h = M[b]->second;
        if(gcd(g, h) == 1) {
            removed.emplace_back(M[b]->first);
            L.erase(M[b]);
            M[b] = L.end(); // 表示已经不在数组中

            if(a == b) break;

            auto u = M[a]; u++;
            if(u == L.end()) u = L.begin();
            Q.emplace(a, u->first);
        }
    }

    cout << removed.size();
    for(auto& x : removed) {
        cout << " " << (x+1);
    }
    cout << '\n';
}

int main() {
    int t; cin >> t;
    list<pair<int,int>> L;
    while(t--) {
        int n; cin >> n;
        L.clear();
        for(int i = 0; i < n; i++) {
            int x; cin >> x;
            L.emplace_back(i, x);
        }
        solve(L, n);
    }
}