CF#771 Div.2 A~C 题解

这篇博客探讨了三种不同的算法问题,涉及序列的最小字典序排列、奇数偶数排序以及逆序对的连边问题。通过贪心策略解决A题,将序列翻转以达到最小字典序;B题中,通过判断奇数和偶数子序列的单调性确定是否能形成单调不降序列;C题利用单调栈处理逆序对,有效地计算连通块数量。这些方法展示了在算法设计中如何利用数据结构优化复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CF #771 div.2

A. Reverse(思维)

题目大意

给定长度为n的序列 可以翻转任意一段 输出他的最小字典序排列

解题思路

因为要最小字典序 所以从n = 1开始向后枚举 如果说q[i] != i 那就说明把 i 换回这里字典序最小(贪心

很重要的一点是因为我的程序鲁棒性不够强 所以导致了数组越界 达到了负的下标

以后一定要注意在模拟reverse的时候不能越界

代码
int q[510], n;

void solve()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> q[i];

    int j = 1;
    while(q[j] == j)    j ++;
    int k = n;
    while(q[k] != j && k > 0)    k --;
    for(int i = j, l = k; i < l; i ++, l --)
        swap(q[i], q[l]);
    
    for(int i = 1; i <= n; i++) cout << q[i] << " ";
    cout << endl;
}   

B. Odd Swap Sort(构造)

题目大意

给定长度为n的数组 当ai + ai+1是奇数时 你可以对其调换位置 这样的操作不限次数 问是否能够将序列变成单调不降序列(ai <= ai+1)

解题思路

ai + ai+1是奇数 代表我们只能调换不同奇偶性的两个相邻元素

所以可以把数组中的元素分为奇数和偶数两个部分

可以发现,如果奇数数组中或是偶数数组中有逆序的情况,是无法通过题目中的特定操作来把数组变为不严格的升序数组的

代码
int n;
vector<int> o, e;//odd even

void solve()
{
    o.clear();e.clear();
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
    {
        int x;scanf("%d", &x);
        if(x & 1)   o.push_back(x);
        else e.push_back(x);
    }

    bool flag = true;
    for(int i = 1; i < o.size(); i++)
    {
        if(o[i] < o[i - 1])
        {
            flag = false;
            break;
        }
    }

    for(int i = 1; i < e.size(); i++)
    {
        if(e[i] < e[i - 1]) 
        {
            flag = false;
            break;
        }
    }   
    if(flag)    puts("YES");
    else puts("NO");
}   

英语积累:non-decreasing order 单调不降序列

C. Inversion Graph(单调栈)

题目大意

为一个长度为n的序列的逆序对连边(如果后面的数小于前面的数,就将这两个数(包括这两个数所在的连通块), 求连通块的个数

解题思路

逆序对

如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。

如果用朴素的做法 就是双循环 但test = 2e5 显然只能使用nlong或者n的算法

我们来观察一下样例

可以发现后一个连通块中的所有数大于前一个连通块中的所有数

以连通块(也就是集合)的角度来思考,如果一个数小于前面连通块的最大数,则将这个数添加至连通块中,否则,这个数作为新的连通块

选取样例

3 2 1 6 5 4
得到的栈 3 - 3 - 3 - 3,6 - 3,6 - 3,6

此时还未万事大吉
3 1 5 2 4
若是按上面的方法分析 3 - 3 - 3,5 - 3,5 - 3,5
但是 答案应该是1 因为2连边到3和5 3和5应该是在一个连通块中的
得出结论:当一个数属于当前连通块时,还应该继续判断是否属于前面的连通块
代码
void solve()
{
    stack<int> stk;// 用单调栈维护每个连通块中最大的元素

    cin >> n;
    for(int i = 0; i < n; i++)
    {
        int x;
        cin >> x;

        if(stk.size() == 0)  stk.push(x);
        else // stk.size()
        {
            if(x > stk.top())   stk.push(x);// 大于当前栈顶直接入栈
            else // 否则保存栈顶,将栈中大于这个数的元素出栈,然后将原栈顶重新入栈
            {
                int t = stk.top();
                while(stk.size() && t > x)  stk.pop();
                stk.push(t);
            }
        }
    }
    cout << stk.size() << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值