Codeforce 2040-C 题解

codeforce 2040 C

小小guess+状压初见杀。
硬控我两个小时的题。

题干传送门
题干大意:请你生成一个包含n个数的排列,要求它是字典序第k个所有连续子数组的最小值之和最大的排列

思路:非常抽象的题干,使我的大脑旋转
首先分解这句话:(字典序第k个((所有((连续子数组)的最小值)之和)最大))的排列。
1 连续子数组:数组的一小段。
2 连续子数组的最小值:这一段数字中最小的那个。
3 所有——之和:把这些最小值加起来。
4 ——最大:要求上一步得到的和最大。
5 字典序第k个:显然,能同样得到最大值的排列似乎不止一个,而且作为排列,可以把它们排序。

也就是说,我们需要聪明地构造排列,让别人“随意取哪一段”,最小值总体上保持最大。也就是说,应该让大的数字尽量多的被取到,小的数字尽量少的被取到。
直观想象一下,似乎中间的元素更容易被取到?一个元素出现的次数应该等于“它左边的元素数量”乘上“它右边的元素数量”,这样如果总元素数恒定,越靠近中间,出现次数越多。
所以我们应该“倾向于”把大的元素放在中间。
但是,决定一个区间贡献多少的,是这个区间的最小值而不是最大值。所以思考以下情况:
如果出现一个“凹陷处”,也就是形如2,1,3这样的部位,则我们可以发现,此时如果交换中间的元素和两侧任意一个元素,最小值的和是会变大的。因为中间的小元素会导致它“压倒”两侧的元素,也就是[2,1],3和2,[1,3]都是1。但如果中间的元素大于一侧,或者大于两侧,则不会出现“压倒”的情况,“决定权”会回到两侧元素手中。

所以,我们要优化掉所有“凹陷处”。一个比较清楚的办法是从大到小取出元素,每次把元素加到当前数组的最前面或者最后面。这样就可以形成没有凹陷的“单峰”。

接下来解决字典序的问题。
首先,如果每次都把元素放在左边,字典序最小。这很容易理解,这种操作会构建一个单调递增的排列,当然是第0个排列。
第1个排列是把拿出的第一个元素(次大值)放在右边,剩下的放在左边。这相当于第0个排列交换最后两个元素。
这种把二元操作按大小依次排列起来的操作就可以应用所谓“二进制拆分”。
将k按二进制拆分成若干位,每一位代表这次取出来的数应该加到左边还是右边。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ed cout << endl
#define _for(l, r) for (ll i = (l); i <= (r); i++)
#define _refor(l, r) for (ll i = (l); i >= (r); i--)

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    int n;
    while (t--)
    {
        ll n;
        ll k;
        cin >> n >> k;
        --k;
        //想要满足条件 序列必须是【单峰值】的
        //1 3 4 6 5 2  true
        //      -
        //1 3 2 4 6 5  false
        //  -     -
        //所以可以这样构造:把最大的放在中间 再逐渐把小的添在两边
        //每次可以选择左边或者右边 所以一共有2^(n-1)种方案
        //如果k大于这个值则直接-1
        if (n - 1 < 64 &&k >= (1ull << (n - 1)))
        {
            cout << "-1"<<endl;
            continue;
        }
        
        deque<ll> ans{n};
        //解决字典序的问题
        //二进制拆分 第0个全放前面 第1个把第一个数字放在后面 以此类推
        _refor(n-1,1)
        {
            if (k & 1)
                ans.emplace_back(i);
            else
                ans.emplace_front(i);
            k >>= 1;
        }
        _for(0,n-1)
            cout << ans[i] << ' ';
        cout << endl;
    }

    return 0;
}
posted @ 2025-05-16 17:51  風笙咲  阅读(32)  评论(0)    收藏  举报
页面特效
正在加载今日诗词....