由于我个人问题对本题做一个题解记录一下
题目在输出的时候做出了一个 l 1 l1 l1 and l 2 l2 l2 长度范围的限定,这对我第一的代码中的 for 循环范围产生了一定设定上的偏差,从左端上看 l 1 l1 l1 and l 2 l2 l2 是必定满足 1 ≤ l 1 , l 2 1≤l1,l2 1≤l1,l2,由于这个条件所以左端是不可能达到 n n n 这个位置的所以范围应该是 1 ≤ l 1 , l 2 ≤ n − 1 1≤l1,l2≤n-1 1≤l1,l2≤n−1
B-Dreamoon Likes Permutation
思维、模拟
给你一段长度为 n n n 的序列,要求你将该序列分为两个部分,分别为序列 p 1 p1 p1 和 p 2 p2 p2,长度分别为 l 1 l1 l1 和 l 2 l2 l2,这两个序列分别满足满足序列中的元素可以通过排列变成连续序列,且元素刚好只出现一次,问能分成多少种,输出每种 l 1 l1 l1 和 l 2 l2 l2 的值。
我原先想到的是set解法,将所有的长度枚举一遍,存储在 set 容器中,因为set容器中去重有序的性质,如果最后一个元素与长度不相符,就说明有重复出现是不符合的,或者是元素不是连续的。
set.clear()
底层的实现就是通过earse删除,时间复杂度O(n)这里给出代码只是一个思路的参考,是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 << '\n';
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 << '\n';
}
else{
cout << ans.size() << '\n';
for(PII v: ans){
cout << v.fi << " " << v.se << '\n';
}
}
}
}
设ma为a中所有元素的最大值。如果可以将a序列分解为两个排列 p 1 p1 p1 and p 2 p2 p2,那么ma必须与 l 1 l1 l1和 l 2 l2 l2中最大的那一个值相等。
所以最多有两种情况:
我们可以分别检查这两种情况,时间复杂度为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() << '\n';
for(PII v : ans){
cout << v.fi << " " << v.se << '\n';
}
}
}
超时的点主要是枚举所有长度的时候超时,所以应该直接判断去做,这里枚举长度所造成的时间复杂度是O( n 2 n^2 n2)超时是必然的
//判断是否访问过
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\n", ans_cnt);
for(int i = 0; i < ans_cnt; i++) {
printf("%d %d\n", ans[i][0], ans[i][1]);
}
}
}