Codeforces Round 797 (Div

Codeforces Round 797 (Div. 3)

Price Maximization

给定\(n\)个商品(n为偶数),每个商品的重量为\(a_i\),你需要将其两两打包,打包的成本为\(\lfloor \frac{a_i+a_j}{k} \rfloor\),\(k\)给定,现在需要最大化打包成本,请你求出最大成本

题解:贪心+双指针

我们尽可能减少浪费,对于每个商品的重量\(a_i\)来说,我们能够产生\(a_i/k\)的贡献,那么被浪费掉的是他们的余数\(a_i\%k\),所以我们尽可能使得打包两个商品时,两个商品的余数能够对答案继续增加贡献,并且我们知道两个商品的余数相加最大只会超过或等于\(k\),根据贪心的思想,我们可以将商品按照对\(k\)取余的大小进行降序排序,那么我们优先处理余数大的数,我们需要帮这个余数大的数找到一个能够产生贡献且浪费最少的数,也就是说我们需要从余数最小的数开始找起,同时我们发现随着遍历较大余数,找到能够的与之匹配的较小余数一定不会是之前遍历过的较小余数,所以说存在单调性,我们可以利用两端双指针实现

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, k;
int a[N];

void solve()
{
    cin >> n >> k;
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        ans += a[i] / k;
        a[i] %= k;
    }
    sort(a + 1, a + n + 1, greater<int>());
    for (int i = 1, j = n; i < j; ++i)
    {
        while (j > i && a[i] + a[j] < k)
            j--;
        if (j > i)
        {
            ans++;
            j--;
        }
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Shifting String

给定字符串\(s\)和排列\(p\),每次操作将\(s\)按照\(p\)重新排列,即\(new_i = s_{p_i}\),询问需要操作几次才能使\(s\)变回原样

题解:思维+循环节+\(kmp\) :好题目

10
codeforces
8 6 1 7 5 2 9 3 10 4
1 2 3 4 5 6 7 8 9  10

观察样例我们得到,实际上在转换过程中该字符串由三个环构成:\((1,8,3),(2,6),(4,7,9,10)\)

那么操作的总次数,我们只需要求出每个环的最小循环节的长度即可,然后其所有环的最小循环节的长度的\(lcm\)就是答案

那么如何求出最小循环节的长度:

我们只要利用\(kmp\)求出最大的\(Border\)也就求出了最小的周期\(p\),我们只要检查是否\(p\ |\ |s|\),如果能够整数就说明字符串\(s\)的最小循环节为\(p = |s| - Border\),否则其最小循环节为\(|s|\)

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n;
int a[N];

void solve()
{
    cin >> n;
    string s;
    cin >> s;
    s = " " + s;
    vector<bool> vis(n + 10);
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    int ans = 1;
    for (int i = 1; i <= n; ++i)
    {
        if (vis[i])
            continue;
        int u = a[i];
        string t;
        while (u != i)
        {
            vis[u] = true;
            t += s[u];
            u = a[u];
        }
        t += s[i];
        int m = t.length();
        t = " " + t;
        vector<int> ne(m + 10, 0);
        for (int i = 2, j = 0; i <= m; ++i)	//kmp求border
        {
            while (j && t[i] != t[j + 1])
                j = ne[j];
            if (t[i] == t[j + 1])
                j++;
            ne[i] = j;
        }
        if (m % (m - ne[m]) == 0)		//判断最小周期p是否为循环节
            ans = lcm(ans, m - ne[m]);
        else
            ans = lcm(ans, m);
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Count the Trains

你有\(n\)节车厢,每节车厢速度为\(a_i\),每节车厢不会超过前面的车厢,例如原来车厢速度为\([10,13,5,2,6]\),那么最后车厢的速度为\([10,10,5,2,2]\),速度相同的车厢组成一辆火车,所以样例中有\(3\)辆火车;现在按照顺序给出\(m\)次询问,每次询问给定\(k,d\)\(a_k:=a_k-d\),即让第\(k\)个车厢减少\(d\)的速度,让你回答每次询问存在几辆火车?

题解:模拟 + \(map\) : 有点东西的题目

一开始以为是维护二元组单调栈,但是很难维护,学习了下\(jiangly\)的代码

按照题意利用\(map\)模拟,首先我们知道如果修改的车厢速度还是比它前面的车厢速度快那就不会造成影响,但是如果比它之前的车厢速度慢,那么他就会和之前的车厢分开并且还要原来带上后面的车厢,这道题让我们给出火车的数量,也就是有多少个不同速度的车厢,所以我们可以利用\(map\)模拟插入车厢的过程:

\(map<int,int> mp\):前键代表火车最前面车厢(火车头)的下标,后键代表该辆火车的速度,\(mp.size()\)代表火车的数量

  1. 如果修改后(插入\(map\)中,就可以得到该车厢属于哪个火车)的车厢速度比该车厢所在的火车速度快,说明对火车数量不会存在影响,我们直接在\(map\)中删除即可
  2. 如果修改后(插入\(map\)中,就可以得到该车厢属于哪个火车)的车厢速度比该车厢所在的火车速度慢,说明会以该插入的车厢为火车头形成新的火车,同时也有可能会使得后面的火车被合并(即插入的车厢作为新的火车的火车头,后面的速度比他快的火车被其合并),那么如何表示后面的火车被合并,实际上我们只需要在刚刚插入的位置后面将所有比它速度快的火车从\(map\)中删除即可
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m;
int a[N];
map<int, int> mp;

void add(int id, int x)
{
    mp[id] = x;
    auto it = mp.find(id);
    if (it != mp.begin() && prev(it)->second <= x)//如果速度比当前的火车快,没有影响
    {
        mp.erase(it);
        return;
    }
    while (next(it) != mp.end() && next(it)->second >= x)//如果速度比当前的火车慢
        mp.erase(next(it));
}

void solve()
{
    mp.clear();
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        add(i, a[i]);
    }
    while (m--)
    {
        int k, d;
        cin >> k >> d;
        a[k] -= d;
        add(k, a[k]);
        cout << mp.size() << " ";
    }
    cout << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2023-03-16 01:02  Zeoy_kkk  阅读(14)  评论(0编辑  收藏  举报