Practice.

A. Two Elevators

题意

给定两个电梯,电梯从一层移动到相邻层需要一秒。当前人位于第一层;第一个电梯位于 \(a\) 层;第二个电梯位于 \(b\) 层,但是该电梯会先前往 \(c\) 层。

输出最先到达第一层的电梯的耗时。

思路

第一个电梯耗时 \(a - 1\),第二个梯子耗时 \(abs(b - c) + c - 1\)

取最小值即可。

时间复杂度:\(O(1)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define fs first
#define sc second

void solve() {
    int a, b , c;
    cin >> a >> b >> c;
    int p = abs(a - 1), q = abs(c - b) + abs(c - 1);
    if(p == q) cout << 3 << '\n';
    else if(p > q) cout << 2 << '\n';
    else cout << 1 << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

b不一定小于c,要取绝对值

B. Decode String

题意

对于一个字符串,给定如下的加密方式:

  1. 遍历所有字符;

  2. 对于一个字符,设 \(x_i\) 为从 \(a\) 开始编号的数值;

  3. 如果 \(x_i \geq 10\),将 \(x_i\) 乘上 \(10\)

  4. 将所有 \(x\) 按顺序拼接起来

现在,给定加密后的结果,输出原字符串。

思路

首先,小于 \(10\) 的数不会出现末尾为 \(0\) 的情况,而出现了末尾是 \(0\),前面两个数就是原字符对应的编号。

所以,我们直接倒序遍历,若找到 \(0\),跳过 \(0\) 并读取前两个数并转换,若不是 \(0\),那么将这个数转换。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define fs first
#define sc second

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    string ans;
    for(int i = n - 1; i >= 0; i --){
        char now = s[i];
        if(now == '0'){
            ans = (char) ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') - 1 + 'a') + ans, i -= 2;
        }else{
            ans = (char) (s[i] - '0' - 1 + 'a') + ans;
        }
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

通俗

C. Jumping on Tiles

题意

给定一个字符串,规定操作为从该位置跳到另一个位置,代价为两个位置对应字符在字母表中位置的差的绝对值。

输出从字符串左端跳到右端的代价和的最小值,以及该代价下能跳过的最多位置(不可以重复跳到同一个位置)。

思路

显然,我们只需按照字典序跳着走即可,那么我们不妨记录每个字母出现的次数以及对应的下标,然后从起点的字母按顺序遍历到终点的字母,边遍历边将所有下标都加入答案中,最后根据答案序列计算代价即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

const int N = 110, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    string s;
    cin >> s;
    int n = s.size();
    vector<vector<int>> a(26);
    for(int i=0;i<n;i++) a[s[i] - 'a'].emplace_back(i);
    vector<int> ans;
    int step = s[0] < s[n - 1] ? 1 : -1;
    for(int i=s[0]-'a';i*step<=((int)(s[n-1]-'a'))*step;i += step){
        for(auto e : a[i]) ans.emplace_back(e);
    }
    int sum = 0;
    for(int i=1;i<ans.size();i++) sum += abs((int) (s[ans[i]] - s[ans[i - 1]]));
    cout << sum << ' ' << ans.size() << '\n';
    for(auto e : ans) cout << e + 1 << ' ';
    cout << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

跳一跳

D. Friends and the Restaurant

题意

给定 \(n\) 个朋友,每个朋友需要花 \(a_i\) 的时间,个人时间预期为 \(b_i\),由此给定长度为 \(n\) 的两个序列 \(a, b\)

定义每次可取出至少两个朋友,只要所有人花费的时长小于等于预期时间的总和,那么这些朋友就可以一起去餐馆吃饭。

输出最多可取出多少对满足条件的朋友,且一个人最多只能在一对朋友里。

思路

首先,我们不难会想到构建一个新的数组 \(c\),满足 \(c_i = b_i - a_i\),然后将其排个序。

我们不妨直接两两配对,因为既然三个人能配对,那么将其拆开去和其他的人组合一定贡献更大。

其次,如果我们从大到小遍历,并从最小值开始从小到大寻找能进行配对的数,那么我们可以发现我们跳过的数对后续的遍历都没有任何贡献。

具体来说,当我们从小到大遍历的时候,既然这个数不满足要求,那么因为另一个数是递减的,我们一定找不出答案。

因此,我们直接贪心地降序排序,然后枚举左端点,移动右指针即可。

时间复杂度:\(O(n \log n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

const int N = 110, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i=0;i<n;i++) cin >> a[i];
    for(int i=0;i<n;i++){
        int cur;
        cin >> cur;
        a[i] = cur - a[i];
    }
    sort(a.rbegin(), a.rend());
    int l = 0, r = n - 1;
    int cnt = 0;
    while(l < r){
        while(l < r && a[l] + a[r] < 0) r --;
        if(l < r && a[l] + a[r] >= 0) cnt ++;
        else break;
        l ++, r --;
    }
    cout << cnt << '\n';
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while(t --) solve();
}

但是升序排序是不可行的哦

E. Guess the Cycle Size

题意

互动游戏。

对于一个由 \(n\) 个节点连接而成的一个无向环,节点的顺序未知。

定义询问为指定两个点,返回值为联结两个点的两条边随机选一,随机是等概率的。如果给定的两个点中至少有一个点超出范围,那么输出 \(-1\)

对于 \(1e18\) 级别的 \(n\),在 \(50\) 次询问内,输出 \(n\) 的值。

思路

这是一道很有趣的题。

首先,这道题没有 \(100\%\) 能通过的算法,但 \(99.999851\%\) 的通过概率可以视为正确。

具体来说,因为随机是等概率的,所以我们可以进行 \(25\) 次询问,每次询问 \(1\ i, i\ 1\),如果两者的返回值不相等,那么我们就找到了答案。

若我们从 \(2\) 开始逐一递增枚举 \(i\),那么只要我们得到了 \(-1\),前一个 \(i\) 就是答案。

这样,我们可以以一个很高的概率 "通过" 此题。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

const int N = 110, inf = 0x3f3f3f3f3f3f3f3f;

int ask(int l, int r){
    cout << "? " << l << ' ' << r << endl;
    cout.flush();
    int x;
    cin >> x;
    return x;
}

void solve(){
    int ans = 0;
    for (int i = 2; i <= 26; i++) {
        long long x = ask(1, i);
        long long y = ask(i, 1);
        if (x == -1) {
            ans = i - 1;
            break;
        }
        if (x != y) {
            ans = x + y;
            break;
        }
    }
    cout << '!' << ' ' << ans << '\n';
    cout.flush();
}

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    //cin >> t;
    while(t --) solve();
}

真的过了吗?真的没过吗?如过

F. Kirei and the Linear Function

题意

给定一个十进制数字构成的字符串 \(s\) 以及一个整数 \(w\),定义 \(v(l, r)\)\(s_{[l ,r]}\) 转为数字后的值,可以有前导零。

现在给定 \(q\) 个询问,每次询问给定 \(l, r, k\),找到最小的 \(L_1, L_2\),满足 \(L_1 \neq L_2, v(L_1, L_1 + w - 1) \times v(l, r) + v(L_2, L_2 + w - 1) \equiv k \pmod 9\)

思路

首先,有一个结论是:一个数模 \(9\) 的值等于其十进制下所有位数字相加之和模 \(9\) 的值。

并且,因为我们求的是模 \(9\) 意义下的值,那么我们可以将所有 \(v(l, r)\) 都模上 \(9\)

那么这题就好办了,我们直接求前缀和 \(sum\),那么 \(v(l, r) = (sum[r] - sum[l - 1]) \bmod 9\)

我们在询问之前,可以预处理所有 \(v(L, L + w - 1) \bmod 9\) 值对应的 \(L\),那么对应询问,我们直接暴力枚举 \(v(L_1, L_1 + w - 1) \bmod 9\),可以发现 \(v(L_2, L_2 + w - 1) \bmod 9\) 是唯一确定的。

那么,我们只要判断 \(v(L_1, L_1 + w - 1) \bmod 9\)\(v(L_2, L_2 + w - 1) \bmod 9\) 是否相等即可,相等取前两个,不相等各自取第一个。

时间复杂度:\(O(n)\)

对应AC代码

#define chatgpt3_5 "bits/stdc++.h"
#define chatgpt4 "bits/extc++.h"

#include chatgpt3_5

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s;
    cin >> s;
    int n = s.size();
    s = " " + s;
    int w, m;
    cin >> w >> m;
    vector<int> sum(n + 1);
    for(int i=1;i<=n;i++) sum[i] = sum[i - 1] + (s[i] - '0');
    vector<vector<int>> ind(9);
    for(int i=1;i+w-1<=n;i++){
        ind[(sum[i + w - 1] - sum[i - 1]) % 9].pb(i);
    }
    while(m --){
        int l, r, k;
        cin >> l >> r >> k;
        int v = (sum[r] - sum[l - 1]) % 9;
        pii ans = {inf, inf};
        for(int i=0;i<9;i++){
            int j = ((k - v * i % 9) % 9 + 9) % 9;
            if(i == j){
                if(ind[i].size() < 2) continue;
                if(ans.fs > ind[i][0]) ans = {ind[i][0], ind[i][1]};
            }else{
                if(ind[i].empty() || ind[j].empty()) continue;
                if(ans.fs > ind[i][0]) ans = {ind[i][0], ind[j][0]};
            }
        }
        if(ans.fs == inf) cout << -1 << ' ' << -1 << '\n';
        else cout << ans.fs << ' ' << ans.sc << '\n';
    }
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

主要是第一个结论

G. Cut Substrings

题意

给定一个字符串 \(s\) 和一个模式串 \(t\),定义一次操作为选定 \(s\) 中一个与模式串 \(t\) 匹配的子串,并将这个子串所有字符修改为 "."。

输出最小的操作数,使得 \(s\) 中没有可以和模式串 \(t\) 匹配的子串,并输出方案数。

思路

因为数据量较小,我们考虑将所有匹配的点都暴力枚举出来。

那么,我们可以得到若干个 \(s\) 上的区间。

可以发现,如果两个区间相交,那么删除任意一个区间都可以让这两个区间无法匹配。

那么反着来说,两个不相交的区间的操作是独立的。

那么我们考虑动态规划,其中 \(dp[i]\) 为必须要选定 \(i\) 的条件下,让前 \(i\) 个区间的无法匹配的最小操作数,\(val[i]\) 为对应的方案数。

我们考虑枚举 \(i\),并找出第一个和 \(i\) 不相交的区间 \(j\),然后枚举和 \(j\) 相交的所有区间 \(k\)(包含 \(j\) 本身)。

可以发现,\(i\)\(j, k\) 不相交,\(j, k\) 相交。那么很显然 \(dp[k] = \min(dp[k], dp[i] + 1)\)

与此对应地,如果 \(dp[k] > dp[i] + 1\),那么 \(val[k] = val[i]\),否则如果 \(dp[k] = dp[i] + 1\),那么 \(val[k] += val[i]\)

但是这里存在一个问题,我们不知道最后的状态在哪一个位置。

因而,我们考虑多放入一个区间,区间左端点为 \(n + m\),即保证不和前面区间相交。那么最后该区间的 \(dp\)\(-1\) 就是我们需要的答案了。

时间复杂度:\(O(n)\)

对应AC代码

#define chatgpt3_5 "bits/stdc++.h"
#define chatgpt4 "bits/extc++.h"

#include chatgpt3_5

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e6 + 10, M = 2e5 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s, t;
    cin >> s >> t;
    int n = s.size(), m = t.size();
    s = " " + s, t = " " + t;
    vector<int> p(1, -inf);
    for (int i = 1; i + m - 1 <= n; i ++) {
        bool f = true;
        for (int j = 1; j <= m; j ++) {
            if (s[i + j - 1] != t[j]) {
                f = false;
                break;
            }
        }
        if (f) p.pb(i);
    }
    p.pb(n + m);
    n = p.size() - 1;
    vector<int> dp(n + 1, inf), val(n + 1);
    dp[0] = 0;
    val[0] = 1;
    for (int i = 0; i < n; i ++) {
        int j = i + 1;
        while (j <= n && p[j] <= p[i] + m - 1) j ++;
        for (int k = j; k <= n && p[k] <= p[j] + m - 1; k ++) {
            if (dp[i] + 1 < dp[k]) {
                dp[k] = (dp[i] + 1) % mod;
                val[k] = val[i];
            } else if (dp[i] + 1 == dp[k]) {
                val[k] = (val[k] + val[i]) % mod;
            }
        }
    }
    cout << dp[n] - 1 << ' ' << val[n] << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

神奇的 \(dp\)