Codeforces Round 987 (Div. 2) A/B/C/D/E

A. Penchick and Modern Monument

题目链接:https://mirror.codeforces.com/contest/2031/problem/A

思路:

分析发现答案就是:\(总个数 - 最长上升子序列的个数\)

时间复杂度: \(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n,m;

void solve(){
    cin>>n;

    vector<int> h(n);
    for(auto &hh:h)cin>>hh;

    int mx=0;
    vector<int> f(n);
    for(int i=0;i<n;i++){
        f[i]=1;
        if(i){
            for(int j=0;j<i;j++){
                if(h[j]<=h[i])f[i]=max(f[j]+1,f[i]);
            }
        }
        mx=max(mx,f[i]);
    }

    cout<<n-mx<<endl;

}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

B. Penchick and Satay Sticks

题目链接:https://mirror.codeforces.com/contest/2031/problem/B

思路:

因为只有相差为 \(1\) 的两个数才能进行互换,所以若不能进行排序,那么存在一个间隔,有 \(间隔前的最大值 - 间隔后的最小值 > 1\) 。所以可以预处理前缀最大和后缀最小在对应索引的值,并借此判断。

时间复杂度: O(n)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n,m;

void solve(){
    cin>>n;

    vector<int> a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];

    vector<int> suf(n+1);

    suf[n]=a[n];
    for(int i=n-1;i;i--){
        suf[i]=min(suf[i+1],a[i]);
    }

    int mx=a[1];
    for(int i=2;i<=n;i++){
        if(mx-suf[i]>1){
            cout<<"No\n";
            return;
        }
        mx=max(mx,a[i]);
    }
    cout<<"Yes\n";
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

C. Penchick and BBQ Buns

原题链接:https://mirror.codeforces.com/contest/2031/problem/C

思路:

思维题。对于偶数序列来说一定可以,而奇数序列则不一定。若存在某个数出现了奇数次,不妨为 \(3\),那么这三个数之间的距离满足: $ a^2 + b^2 = c^2 $,那么显然最小的满足要求的距离为 \(a=9\) $ b=16$ \(c=25\)
对于长度为 \(27\) 的序列可以构造:1 3 3 4 4 5 5 6 6 1 2 7 7 8 8 9 9 10 10 11 11 12 12 13 13 1 2,那么大于 \(27\) 的序列也可进行构造。

所以,仅有长度小于 \(27\) 的且长度为奇数的序列无法构造。

时间复杂度:O(n)

代码

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n,m;

void solve(){
    cin>>n;

    if(n&1){
        if(n<27){
            cout<<-1<<endl;
        }else{
            vector<int> ans(n+1,0);
            ans[1]=1,ans[10]=1,ans[11]=2,ans[26]=1,ans[27]=2;
            for(int i=1,j=3;i<=n;i++){
                if(ans[i]) cout<<ans[i]<<' ';
                else{
                    cout<<j<<' '<<j++<<' ';
                    i++;
                }
            }
        }
    }else{
        for(int i=1,j=1;i<=n;i+=2,j++){
            cout<<j<<' '<<j<<' ';
        }
        cout<<endl;
    }
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

D. Penchick and Desert Rabbit

原题链接: https://mirror.codeforces.com/contest/2031/problem/D

思路:

兔子可以从当前位置跳到前缀中高于当前的位置,也可以从当前位置跳到后缀低于当前的位置。因为要找到每个位置可达的最高位置,开始考虑对通过题意进行构图,但发现递减序列的边的空间复杂度为 \(O(n^2)\),显然无法建图暴力搜索得到答案。

考虑对问题进行转化,转化为区间问题,通过区间合并+并查集对问题进行解决

转化过程:遍历整个序列每次遇到一个高于前缀最高的位置,我们就建立一个集合,直到下一个这种位置之前,显然每个集合的第一个位置是最高的位置,且集合内的各个位置可以通过这个位置相互转化,并且每个集合都存在一个最高点和最低点。最终从左到右,每个集合的最高点依次递增,但最低点并不确定。若两个集合间的位置可以相互转化,那么两个集合对应的最高最低区间一定有至少两个交点。所以便可对区间进行排序,然后区间合并,将较小最高的集合链接到较高的集合,这个操作可以通过并查集进行实现。

时间复杂度 O(n)

代码

// 转化为区间问题了

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n,m;
struct Range{
    int id;
    int low,up;
    bool operator<(const Range& t)const{ return low<t.low; }
};

int find(vector<int> &p,int x){
    if(p[x]!=x)return p[x]=find(p,p[x]);
    return x;
}

void solve(){
    cin>>n;

    vector<int> h(n+5);
    for(int i=1;i<=n;i++) cin>>h[i];

    int idx=0,maxp=0,low;
    vector<Range> range;
    vector<int> id(n+5),mh(n+5);

    for(int i=1;i<=n;i++){
        if(h[i]>h[maxp]){ // 如果大于就新建一个区间
            if(maxp) range.push_back({idx,low,h[maxp]});
            maxp=i;
            low=h[i];
            mh[++idx]=h[i];
        }else low=min(low,h[i]); // 更新最小
        id[i]=idx; // 节点i所属区间编号
    }
    range.push_back({idx,low,h[maxp]});

    // 按最低点排序
    sort(range.begin(),range.end());

    vector<int> p(idx+1);
    for(int i=1;i<=idx;i++)p[i]=i;
    
    // 区间合并 利用并查集 将较小maxh值的合并到较大的上
    int ed=-1,now=-1;
    for(auto [x,lo,up]:range){
        if(ed<=lo) ed=up,now=x;
        else{
            if(up>ed){
                swap(x,now);
                ed=up;
            }
            p[x]=now;
        }
    }

    for(int i=1;i<=n;i++){ 
        cout<<mh[find(p,id[i])]<<' ';
    }
    cout<<endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}

E. Penchick and Chloe's Trees

原题链接: https://mirror.codeforces.com/contest/2031/problem/E

思路:

这题实际上就是把给的图转化为二叉树,然后看最小可能深度是多少。所以对于节点儿子大于两个的位置需要进行转化,使其深度尽可能小,发现类似于哈夫曼树,所以直接 \(DFS\) + 哈夫曼。

时间复杂度 O(nlogc)

代码:

#include<bits/stdc++.h>
using namespace std;    

typedef long long ll;
typedef pair<int,int> PII;

const int N=2e5+10;

int n;
vector<vector<int>> e;

inline void add(int a,int b){
    e[a].push_back(b);
}

int dfs(int x,int father){   
    priority_queue<int,vector<int>,greater<int>> heap;
    
    for(auto y:e[x]){
        if(y==father)continue;
        int s=dfs(y,x);
        heap.push(s);
    }

    if(heap.empty()) return 1;
        
    while(heap.size()>2){
        auto p1=heap.top(); heap.pop();
        auto p2=heap.top(); heap.pop();
        heap.push(p2+1);
    }

    int mx=0;
    while(!heap.empty()){
        mx=heap.top();
        heap.pop();
    }

    return mx+1;
}

void solve(){
    cin>>n;

    e=vector<vector<int>>(n+1);

    int a;
    for(int b=2;b<=n;b++){
        cin>>a;
        add(a,b),add(b,a);
    }

    int ans=dfs(1,-1);

    cout<<ans-1<<endl;
}

int main() {
    cin.tie(0)->sync_with_stdio(false);
    cout.tie(0);

    int t=1;
    cin>>t;

    while(t--)solve();
}
posted @ 2025-02-24 20:24  宋佳奇  阅读(11)  评论(0)    收藏  举报