Codeforces 513B2 or 513B1 Permutations 策略题

题目链接:

B1难度 http://codeforces.com/contest/513/problem/B1 (n <= 8)

B2难度 http://codeforces.com/contest/513/problem/B2 (n <= 50)

题目大意:

对于一个排列p个定了函数f(p)的定义, 为所有的1 <= l <= r <= n的数对[l, r]中 min(a[l], a[l + 1], ... a[r]) 的和(一共是n*(n + 1)/2个数的和)

现在对于n个数的排列, 输出在满足f(p)最大的排列中字典序从小到大排在第m个的排列

例如n = 3的时候, 所有f(p)最大的排列有:

1, 2, 3

1, 3, 2

2, 3, 1

3, 2, 1

一共4种, 他们的f(p)都是1 + 2 + 3 + 2 + 2 + 1 = 11

当m = 2时输出第二个也就是1, 3, 2


大致思路:

想了好久的策略题, 终于还是想出来了, 首先不难发现小的数要尽量往左边或者右边放, 一次放置1, 2,.. n

放置1的时候可以是最左边或者最右边, 然后放置2, 放在剩下位置的最左边或者最右边, 3同理

而且不难发现n个数的排列当中满足f(p)最大的刚好有2^(n - 1)种(不难用动态规划证明)

这里给出4的所有可能:

1, 2, 3, 4

1, 2, 4, 3

1, 3, 4, 2

1, 4, 3, 2

2, 3, 4, 1

2, 4, 3, 1

3, 4, 2, 1

4, 3, 2, 1

而每一次决定放置数字i的时候放在左边和放在右边的字典序不一样, 刚好评分剩余的2^(n - 1)/ (2^i)中情况

所以讨论一下剩余的m与每次放在左边时的剩余的字典序的个数来判断放在左边还是右边即可


代码如下:

B1难度 Result  :  Accepted     Memory  :  0 KB     Time  :  15 ms

B2难度 Result  :  Accepted     Memory  :  0 KB     Time  :  31 ms

/*
 * Author: Gatevin
 * Created Time:  2015/2/25 15:55:43
 * File Name: poi~.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;


int main()
{
    /*
    for(int i = 1; i <= 4; i++)
    {
        for(int j = 1; j <= i; j++)
            cout<<dp[i][j] << " ";
        cout<<endl;
    }
    */
    int n;
    lint m;
    scanf("%d %I64d", &n, &m);
    if(n == 1)
    {
        printf("1\n");
        return 0;
    }
    int a[110];
    lint all = (1LL << (n - 1));
    int fir = 1, end = n;
    for(int i = 1; i <= n; i++)
    {
        if(m > (all >> 1))
        {
            a[end] = i;
            end--;
            m -= (all >> 1);
            all >>= 1;
            continue;
        }
        a[fir] = i;
        fir++;
        all >>= 1;
    }
    for(int i = 1; i <= n; i++)
        printf("%d ", a[i]);
    return 0;
}


### Codeforces 目列表获取方式 对于希望在Codeforces上寻找目进行练习的用户而言,可以通过访问Codeforces官方网站并导航至竞赛页面来浏览不同类型的目。具体来说,在网站主页上方菜单栏中选择“Problemset”,这里包含了大量历史比赛中的目供用户筛选[^1]。 为了更高效地找到适合自己的练习,可以利用页面右侧的一系列过滤条件,比如通过标签(Tags)、难度等级(Rating)或是特定的比赛名称来进行精准查找。例如,如果对组合数学感兴趣或者想要挑战与之相关的算法问,“Combinatorics”就是一个不错的标签选项;而针对新手用户,则可以从较低评级开始尝试逐步提升难度。 另外值得注意的是,除了直接搜索之外,还可以关注官方举办的各类赛事公告以及社区内其他用户的推荐资源,这些都能帮助发现更多优质习。 ```python import requests from bs4 import BeautifulSoup def fetch_codeforces_problems(): url = 'https://codeforces.com/problemset' response = requests.get(url) soup = BeautifulSoup(response.text,'html.parser') problems_table = soup.find('table',{'class':'problems'}) rows = problems_table.findAll('tr')[1:] # 跳过表头 result = [] for row in rows: cols = row.findAll('td') problem_info = { "index": cols[0].text.strip(), "name": cols[1].a['title'], "link": f"https://codeforces.com{cols[1].a['href']}" } result.append(problem_info) return result[:5] # 返回前五个作为示例 fetch_codeforces_problems() ``` 此段Python代码展示了如何爬取Codeforces Problemset页面的部分数据,并将其整理成易于阅读的形式返回给调用者。当然实际应用时还需要考虑异常处理等问以确保程序稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值