Codeforces Round #681 (Div. 2, based on VK Cup 2019-2020 - Final) [A - F]

题目来源

https://codeforces.ml/contest/1443

A. Kids Seating

  在 [1, 4n]这个区间内寻找n个数字,使得这n个数两两不互质,两两不互相可以整除。

思路分析

   从2n+2开始,每隔2取一个数字即可。

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5 + 7;
int a[maxn];
vector <int> ans;
bool vis[maxn];

int main(){
    int t; scanf("%d", &t);
    while (t --){
        int n; scanf("%d", &n);
        int tmp = 4 * n;
        for (int i=1; i<=n; i++){
            printf("%d ", tmp);
            tmp -= 2;
        }
        printf("\n");
    }
    return 0;
}

B. Saving the City

  给一个数字序列。有两种操作,将某一位置上的'0'变成'1',这一步需要$b$费用;第二种操作是将一段连续的‘1’变成'0',这一步需要a费用。问最后将所有的数组都变成'0'的最小费用。

思路分析

  统计两段‘1’序列之间的‘0’的个数p,比较p * b 和 a大小。即对于一段0序列,判断与前后一起消去以及前后两段1序列各自消去的费用的最小值。

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5 + 7;
char ch[maxn];

int main(){
    int t; scanf("%d", &t);
    while (t --){
        int a, b; scanf("%d%d", &a, &b);
        int ans = 0;
        bool flag = false;
        scanf("%s", ch);
        int ln = (int)strlen(ch);
        int num = 0;
        for (int i=0; i<ln; i++){
            if (ch[i] == '1'){
                if (!flag) ans += a;
                flag = true;
                ans += min(a, b * num);
                num = 0;
            }else if (flag) num ++;
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

C. The Delivery Dilemma

   有n家餐厅,有两种派送方式,第一种是他配送,需要a[i]时间;第二种是自己去取,需要b[i]时间。若多家餐厅选择方式二进行派送,则需要自己一家一家取。问最少的时间费用是多少。

思路分析

  可以想到的是,答案的取决于最大的派送时间,以及自己取的时间和。根据商家派送时间进行排序,然后贪心枚举每一次两个时间即可。

#include <bits/stdc++.h>

#define int long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e5 + 7;
//int a[maxn], b[maxn];

struct node{
    int a, b;
};

node tt[maxn];

bool cmp(const node& u, const node& v){
    if (u.a != v.a) return u.a > v.a;
    else return u.b > v.b;
}

signed main(){
    int t; scanf("%lld", &t);
    while (t --){
        int n; scanf("%lld", &n);
        for (int i=1; i<=n; i++) scanf("%lld", &tt[i].a);
        for (int i=1; i<=n; i++) scanf("%lld", &tt[i].b);
        sort(tt+1, tt+1+n, cmp);
        int mmax = tt[1].a;
        int ans = 0, res = INF;
        for (int i=1; i<=n; i++){
            mmax = tt[i].a;
//            cout << res << " " << mmax << " " << ans << endl;
            res = min(res, max(mmax, ans));
            ans += tt[i].b;
        }
        res = min(res, ans);

        printf("%lld\n", res);
    }
    return 0;
}

D. Extreme Subtraction

   给定一个数字序列,每一次可以从头选择一个连续区间进行-1, 也可以从尾部选择一个连续区间进行-1,问最后能否在多次操作之后,使得整个数字序列变成0。

思路分析

   给区间进行一个增加减少的操作,可以联想到差分的思想。然后按照差分的思想就能找到最终的解法。

#include <bits/stdc++.h>

#define int long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e5 + 7;

int a[maxn];
int p[maxn];

signed main(){
    int t; scanf("%lld", &t);
    while (t --){
        int n; scanf("%lld", &n);
        for (int i=1; i<=n; i++) scanf("%lld", &a[i]);
        for (int i=1; i<=n; i++) p[i] = a[i] - a[i-1];
        int ans = 0;
        for (int i=2; i<=n; i++) if (p[i] < 0) a[1] += p[i];
        if (a[1] >= 0) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

E. Long Permutation

  给定一个全排列的长度n,表示为1到n的一个全排列。有两种操作。第一种操作是询问此时排列中,[l, r]这个区间的区间和为多少。另一种操作为将此时的全排列向后递增x次。
  第二种的操作具体可以表现为,当n=3时,第一组全排列为 1 2 3 。 第二组为 1 3 2。依次类推。

思路分析

  首先区间和可以使用线段树去实现。而对于第二种操作,根据数据范围可以想到的是他最多只会改变最后的14个数字。然后就可以根据逆康拓展开,求出此时的全排列,然后逐一的进行单点修改。

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <map>

#define int long long
using namespace std;
const int maxn = 2e5 + 7;
int a[maxn];
int frac[maxn], tree[maxn << 2];
void init(){
    frac[0] = 1;
    int p = 0;
    for (int i=1; i<maxn; i++){
        frac[i] = frac[i-1] * i;
        p = i;
        if (frac[i] > 2e10) break;
    }
//    cout << "P:  " << p << endl;
}

void build(int p, int l, int r){
    if (l == r){
        tree[p] = a[l]; return;
    }
    int mid = l + r >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}

int ask(int p, int l, int r, int L, int R){
    if (L <= l && r <= R) return tree[p];
    int mid = l + r >> 1;
    int ans = 0;
    if (mid >= L) ans += ask(p << 1, l, mid, L, R);
    if (mid < R) ans += ask(p << 1 | 1, mid+1, r, L, R);
    return ans;
}

void update(int p, int l, int r, int x, int y){
    if (l == r){
        tree[p] = y; return;
    }
    int mid = l + r >> 1;
    if (mid >= x) update(p << 1, l, mid, x, y);
    else if (mid < x) update(p << 1 | 1, mid + 1, r, x, y);
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}

void decantor(int x, int n, int m)
{
    vector<int> v;
    vector<int> _a;
    for(int i=1;i<=n;i++)
        v.push_back(i);
    for(int i=n;i>=1;i--)
    {
        int r = x % frac[i-1];
        int t = x / frac[i-1];
        x = r;
        sort(v.begin(),v.end());
        _a.push_back(v[t]);
        v.erase(v.begin()+t);
    }
    
//    int ln = (int)_a.size();
    int p = m - n + 1;
    for (int i=p; i<=m; i++){
        a[i] = _a[i - p];
        update(1, 1, m, i, _a[i-p] + p - 1);
    }
}

int num = 0;

signed main(){
    init();
    int n, q;
    scanf("%lld%lld", &n, &q);
    for (int i=1; i<=n; i++) a[i] = i;
    build(1, 1, n);
    while (q --){
        int op; scanf("%lld", &op);
        if (op == 1){
            int l, r; scanf("%lld%lld", &l, &r);
            printf("%lld\n", ask(1, 1, n, l, r));
        }else{
            int x; scanf("%lld", &x);
            num += x;
            if (n >= 15) decantor(num, 15, n);
            else decantor(num, n, n);
        }
    }
    return 0;
}

F. Identify the Operations

  给定一个长度为n的a数组,其中所有数字是从1到n,各不相同。再给定一个b数组,表示需要筛选出来的数组。每一次操作,是在a数组中寻找一个位置p,将其两边的数字中的某一个数字筛选出来,并将p号位置上的数字删去。问有多少种操作排列组合能够最终筛选最终答案。
  注:a, b数组中的数都各不相同。

思路分析

  对于选出一个数字放入到最终的答案队列中,需要将这个数字两边的某一个数字删去。而我们不能删去b数组后面的数。
  最终答案为过程中,各个数字筛选出来的结果数的乘积。而不能删选的结果数字为后续需要被筛选出来的那些数字。而对于被删掉的数字,其实不需要标记,因为当一个数字删除之后,其前后的位置会重新顶上来,这个位置也就被重新激活。

#include <bits/stdc++.h>

#define int long long
using namespace std;
const int maxn = 2e5 + 7;
const int mod = 998244353;
int a[maxn], b[maxn], p[maxn];
bool vis[maxn];

signed main(){
    int t; scanf("%lld", &t);
    while (t --){
        int n, k; scanf("%lld%lld", &n, &k);
        for (int i=1; i<=n; i++) vis[i] = false;
        vis[0] = true; vis[n+1] = true;
        for (int i=1; i<=n; i++){
            scanf("%lld", &a[i]);
            p[a[i]] = i;
        }
        for (int i=1; i<=k; i++){
            scanf("%lld", &b[i]);
            vis[p[b[i]]] = true;
        }
        int ans = 1;
        for (int i=1; i<=k; i++){
            int tmp = 0;
            if (!vis[p[b[i]] + 1]) tmp ++;
            if (!vis[p[b[i]] - 1]) tmp ++;
            vis[p[b[i]]] = false;
            ans = ans * tmp % mod;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值