2023JSCPC(江苏省赛)vp&补题记录

摘要

5题654罚时,一开始做的挺快的,一个半小时就四题了,然后被求数学期望的银题狠狠痛击了,另一道调了两个小时wa了9发才过掉。最后一小时微距,提前下班了。
省赛在即,加油加油。

赛时部分

I - Elevator

数学

直接输出 \(n-m+1\) 即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

void solve() {
    ll n, m;
    cin >> n >> m;
    cout << n-m+1 << endl;
}

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

J - Similarity (Easy Version)

枚举

注意到数据量很小,直接枚举求出每两个字符串的相似值同时维护最大值即可。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

int get_same(string a, string b) {
    int ans = 0;
    for(int i = 0; i < a.size(); i ++ ) {
        char c = a[i];
        for(int j = 0; j < b.size(); j ++ ) {
            if(b[j] != c) continue;
            int i_ = i+1, j_ = j+1;
            int sum = 1;
            while(a[i_] == b[j_] && i_ < a.size() && j_ < b.size()) {
                sum++;
                i_++, j_++;
            } 
            ans = max(ans, sum);
        }
    }
    return ans;
}

void solve() {
    int n;
    cin >> n;
    vector<string> alls(n);
    for(int i = 0; i < n; i ++ ) cin >> alls[i];
    int ans = 0;
    for(int i = 0; i < n; i ++ ) {
        for(int j = i+1; j < n; j ++ ) {
            ans = max(ans, get_same(alls[i], alls[j]));
        }
    }
    cout << ans << endl;
}

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

H - Neil's Machine

模拟

如果单纯根据题目进行模拟,在更新字符串的部分容易超时,需要考虑如何在模拟的过程中进行优化。
我们注意到每次操作都是对字符串的一整个后缀进行操作,也就是说当我们枚举到某一个不相同的字母时,以这个字母开头的字符串后缀在此之前变化的数值是相等的,因此我们可以维护一个 \(sum\) 来表示枚举到当前字符时,后面所有的字符的变化量。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

void solve() {
    int n;
    cin >> n;
    string a, b;
    cin >> a >> b;
    int ans = 0;
    int sum = 0;
    for(int i = 0, j = 0; i < n; i ++, j ++ ) {
        int A = a[i]-'a', B = b[j]-'a';
        if(A == (B+sum)%26) continue;
        sum += (A > (B+sum)%26 ? A-(B+sum)%26 : (A+26)-(B+sum)%26);
        ans++;
    }
    cout << ans << endl;
}

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

A - Today's Word

模拟

观察字符串变化方式,不难得出,当字符串的长度大于等于 \(2 \ast m\) 时,长度为 \(m\) 的后缀将不再改变。因此我们只需要维护从 \(S_0\) 到长度大于等于 \(2*m\) 的字符串的模拟次数 \(t\),然后求出 \((10^{100}-t)\%26\) 作为偏移量加到长度为 \(m\) 的后缀上即为答案。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

void solve() {
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    ll i = 1;
    int cnt = 0;
    for(i = 1; s.size() <= 2*m; i ++ ) {
        int len = s.size()/2;
        string temp = s.substr(0, len);
        temp += s;
        for(int j = len; j < s.size(); j ++ ) {
            s[j]++;
            if(s[j] > 'z') s[j] = 'a';
        }
        temp += s.substr(len, len);
        s = temp;
    }
    string temp = s.substr(s.size()-m, m);
    int t;
    int sum = 1;
    for(int num = 1; num <= 100; num++) {
        sum = (sum*10)%26;
    }
    while(sum < i) sum += 26;
    t = (sum-i)%26;
    for(int j = 0; j < temp.size(); j ++ ) {
        temp[j] = (temp[j]-'a'+t+1)%26+'a';
    }
    cout << temp << endl;
}

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

K - Similarity (Hard Version)

思维,构造

先考虑特殊情况,当 \(m=0\) 时需要让所有的字符串都没有重复字符,不难想到只要让第 \(i\) 个字符串全由 \('a'+i-1\) 组成即可。而我们知道,小写字母只有\(26\)个,也就是说当 \(m=0\)\(k > 26\) 时无解。

同时,不难看出,当 \(m>=k\) 时同样无解。

接着对于一般情况,我们可以先构造一个长度为 \(k\),并且全为 \(a\) 的字符串。对于第二个字符串我们可以先让前 \(m\) 个字母为 \(a\),其余字母为 \(b\)

而对于后面的字符串,我们可以考虑以 acacac......,bdbdbd......的形式构造,这样构造出来的字符串相似度最大为1(注意要避开ab组合).

 #include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "Yes\n"
#define N cout << "No\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    if(m == 0) {
        if(n <= 26) {
            Y;
            for(int i = 0; i < n; i ++ ) {
                for(int j = 1; j <= k; j ++ ) {
                    cout << (char)('a'+i);
                }
                cout << endl;
            }
        } else {
            N;
        }
        return;
    }
    if(m >= k) {
        N;
        return;
    }
    Y;
    vector<string> ans;
    for(int i = 0; i < 25; i ++ ) {
        for(int j = i+1; j < 25; j ++ ) {
            string temp;
            temp.push_back('a'+i);
            temp.push_back('a'+j);
            ans.push_back(temp);
            if(ans.size() == n) goto nx;
        }
    }
    nx: ;
    for(int i = 0; i < k; i ++ ) cout << 'a';
    cout << endl;
    for(int i = 0; i < m; i ++ ) cout << 'a';
    for(int i = 0; i < k-m; i ++ ) cout << 'b';
    cout << endl;
    for(int i = 2; i < n; i ++ ) {
        string temp;
        for(int t = 1; t <= k/2; t ++ ) {
            temp += ans[i];
        }
        if(k%2) temp += ans[i][0];
        cout << temp << endl;
    }
}

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

补题部分

F - Timaeus

数学,dp

期望是试验中每次可能结果的概率乘以其结果的总和。

我们考虑用 \(dp_i\) 来表示剩下 \(i\) 个材料时的最大期望数量。

因为每次只能选择一种buff:

  1. \(P\%\) 的概率合成双倍物品;
  2. \(Q\%\) 的概率返还一个物品;

所以对于每一个 \(dp_i\) 可以由前面的某种情况选择其中一个buff转移而来。

得到方程:

\[dp_i=max\{P \ast (dp_{i-B}+2)+(1-P) \ast (dp_{i-B}+1), Q \ast (dp_{i-B+1}+1)+(1-Q) \ast (dp_{i-B}+1)\} \]

此外,当 \(B=1\) 时,只使用第二种buff是最优的,即有 \(Q\) 概率不消耗材料。

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pll pair<ll, ll> 

#define endl '\n'
#define Y cout << "YES\n"
#define N cout << "NO\n"

#define fi first
#define se second

const int MOD = 1e9+7, inf = 0x3f3f3f3f;

void solve() {
    int a, b;
    double p, q;
    cin >> a >> b >> p >> q;
    p /= 100, q /= 100;
    vector<double> f(a+1, 0);
    for(int i = b; i <= a; i ++ ) {
        f[i] = max(p*(f[i-b]+2)+(1-p)*(f[i-b]+1), q*(f[i-b+1]+1)+(1-q)*(f[i-b]+1));
    }
    double ans;
    if(b == 1) ans = max(a*1.0/(1-q), f[a]);
    else ans = f[a];
    cout << fixed << setprecision(12) << ans << endl;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int t = 1;
    //cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2025-05-16 10:34  算法蒟蒻沐小白  阅读(100)  评论(0)    收藏  举报