CodeForces

前序

由于我个人问题对本题做一个题解记录一下
在这里插入图片描述
题目在输出的时候做出了一个 l1l1 and l2l2 长度范围的限定,这对我第一的代码中的 for 循环范围产生了一定设定上的偏差,从左端上看 l1l1 and l2l2 是必定满足 1l1l21≤l1,l2,由于这个条件所以左端是不可能达到 nn 这个位置的所以范围应该是 1l1,l2n11≤l1,l2≤n-1

题目链接

B-Dreamoon Likes Permutation

题目类型

思维、模拟

题意简述

给你一段长度为 nn 的序列,要求你将该序列分为两个部分,分别为序列 p1p1p2p2,长度分别为 l1l1l2l2,这两个序列分别满足满足序列中的元素可以通过排列变成连续序列,且元素刚好只出现一次,问能分成多少种,输出每种 l1l1l2l2 的值。

题解

first(TLE-3)

我原先想到的是set解法,将所有的长度枚举一遍,存储在 set 容器中,因为set容器中去重有序的性质,如果最后一个元素与长度不相符,就说明有重复出现是不符合的,或者是元素不是连续的。

时间复杂度

  • 每个长度需要枚举一遍也就是O(n)
  • 每个次分成的两个序列都需要插入set也就是 O(n)
  • 每次set中count查询出现次数都是 o(log n),个人觉得卡的就是查询
  • 每次还要对set进行清除,set.clear()底层的实现就是通过earse删除,时间复杂度O(n)
    最后总的时间复杂度为O(nlogn)

代码

这里给出代码只是一个思路的参考,是TLE的,要注意,应该是可以用 unordered_map 去进行一个优化,但我是懒了

const int maxn = 2e5+50;
set<int> ap;
int a[maxn];
vector<PII> ans;
int main(){
    int t;
    for(scanf("%d",&t);t;t--){
        ans.clear();
        int n; RD(n);
        REP(i, n) RD(a[i]);
        for(int len = 1; len <= n - 1; len++){
            ap.clear();
            bool judge = true;
            //cout << "len :" << len << '
';
            for(int i = 0; i < len; ++i){
                ap.insert(a[i]);
            }
            int tmp;
            tmp = *(--(ap.end()));
            if (ap.size() != len || ap.size() != tmp) continue;
            ap.clear();
            for(int i = len; i < n; i++){
                ap.insert(a[i]);
            }
            tmp = *(--(ap.end()));
            if (ap.size() != n - len || ap.size() != tmp) continue;
            ans.PB(MP(len,n - len));
        }
        if (ans.size() == 0) {
            cout << 0 << '
';
        }
        else{
            cout << ans.size() << '
';
            for(PII v: ans){
                cout << v.fi << " " << v.se << '
';
            }
        }
    }   
}

second

官方解答

在这里插入图片描述
设ma为a中所有元素的最大值。如果可以将a序列分解为两个排列p1p1 and p2p2,那么ma必须与l1l1l2l2中最大的那一个值相等。
所以最多有两种情况:

  1. l1=ma,l2=nmal1 = ma, l2 = n - ma
  2. l2=ma,l1=nmal2 = ma, l1 = n - ma

我们可以分别检查这两种情况,时间复杂度为O(n)。

代码

写法一(TLE)

//判断是否访问过
const int MAXN = 2e5+50;
int used[MAXN];
bool judge_used(int a[], int n){
    

    REP(i, n+1) used[i] = 0;
    REP(i, n) used[a[i]] = 1;
    FOR_1(i, 1, n) if (!used[i]){return false;}
    return true;

}

vector<PII> ans;
const int maxn = 2e5+50;
int a[maxn];

int main(){
    int t;
    for(scanf("%d", &t); t; t--){
        ans.clear();
        int n, ma = 0; RD(n);
        REP(i, n) { RD(a[i]);  ma = max(ma, a[i]);}
        for(int len1 = 1; len1 <= n - 1; ++len1){
            if (judge_used(a, len1) && judge_used(a + len1, n - len1)){
                if (ma == len1 || ma == n - len1) ans.PB(MP(len1, n - len1));
            }
        }
        cout << ans.size() << '
';
        for(PII v : ans){
            cout << v.fi << " " << v.se << '
';
        }
    }
}

超时的点主要是枚举所有长度的时候超时,所以应该直接判断去做,这里枚举长度所造成的时间复杂度是O(n2n^2)超时是必然的

正确代码

//判断是否访问过
const int MAXN = 2e5+50;
int a[MAXN];
int used[MAXN];
int ans[MAXN][2];
int ans_cnt;
bool judge_used(int a[], int n){
    REP(i, n+1) used[i] = 0;
    REP(i, n) used[a[i]] = 1;
    FOR_1(i, 1, n) if (!used[i]){return false;}
    return true;

}
bool judge(int len1, int n){
    return judge_used(a, len1) && judge_used(a + len1, n - len1);
}

int main(){
    int t;
    for(scanf("%d", &t); t; t--){
        ans_cnt = 0;
        int n, ma = 0; RD(n);
        REP(i, n) { RD(a[i]);  ma = max(ma, a[i]);}
        if(judge(n - ma,n)) {
            ans[ans_cnt][0] = n - ma;
            ans[ans_cnt++][1] = ma;
        }
        if(ma * 2 != n && judge(ma,n)) {
            ans[ans_cnt][0] = ma;
            ans[ans_cnt++][1] = n - ma;
        }
        printf("%d
", ans_cnt);
        for(int i = 0; i < ans_cnt; i++) {
            printf("%d %d
", ans[i][0], ans[i][1]);
        }
    }
}
原文地址:https://www.cnblogs.com/ygbrsf/p/13040457.html