前序
由於我個人問題對本題做一個題解記錄一下
題目在輸出的時候做出了一個 and 長度範圍的限定,這對我第一的代碼中的 for 循環範圍產生了一定設定上的偏差,從左端上看 and 是必定滿足 ,由於這個條件所以左端是不可能達到 這個位置的所以範圍應該是
題目鏈接
題目類型
思維、模擬
題意簡述
給你一段長度爲 的序列,要求你將該序列分爲兩個部分,分別爲序列 和 ,長度分別爲 和 ,這兩個序列分別滿足滿足序列中的元素可以通過排列變成連續序列,且元素剛好只出現一次,問能分成多少種,輸出每種 和 的值。
題解
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 << '\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';
}
}
}
}
second
官方解答
設ma爲a中所有元素的最大值。如果可以將a序列分解爲兩個排列 and ,那麼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() << '\n';
for(PII v : ans){
cout << v.fi << " " << v.se << '\n';
}
}
}
超時的點主要是枚舉所有長度的時候超時,所以應該直接判斷去做,這裏枚舉長度所造成的時間複雜度是O()超時是必然的
正確代碼
//判斷是否訪問過
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]);
}
}
}