Contestant. Rank 263. Rating +90.

A. Sum of Three

题意

给定一个整数 \(n\),构造一种方案,使得 \(n = x + y + z\),且 \(x \neq y \neq z; x, y, z \not \equiv 0 \pmod 3\)

思路

分类讨论。

首先,最小的构造是 \(x = 1, y = 2, z = n - 3\),那么显然,\(n - 3 > 2\),即 \(n \geq 6\)

并且,因为 \(z\) 不可以是 \(3\) 的倍数,所以 \(n\) 也不可以是 \(3\) 的倍数。

那么,如果 \(n\)\(3\) 的倍数,我们考虑次小的构造:\(x = 1, y = 4, z = n - 5\),此时 \(n - 5 > 4\),即 \(n \geq 10\)

此时,可以发现 \(n - 5\) 不是 \(3\) 的倍数,从而符合要求。

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

对应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 = 2e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    int n;
    cin >> n;
    if(n % 3 == 0){
        if(n <= 9) {
            cout << "NO\n";
            return;
        }
        cout << "YES\n";
        cout << 1 << ' ' << 4 << ' ' << n - 5 << '\n';
    }else{
        if(n <= 5) {
            cout << "NO\n";
            return;
        }
        cout << "YES\n";
        cout << 1 << ' ' << 2 << ' ' << n - 3 << '\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();
}

好久没有 \(A\) 写这么久了(

B. Fear of the Dark

题意

给定一个目标点,以及两个光源。输出最小的光源半径,使两个光源将从原点 \((0, 0)\) 到目标点的任意一条路径全都照亮。

思路

显然,半径更大,能照亮的概率更大,具有单调性,从而我们考虑二分。

我们按照下面的方式检查半径是否合法:

  1. 任意一个光源到目标点的距离小于半径;

  2. 任意一个光源到原点的距离小于半径;

  3. 任意一个光源覆盖原点和目标点,或者两个光源的距离小于半径之和

时间复杂度:\(O(n \log 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 = 2e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    int px, py, ax, ay, bx, by;
    cin >> px >> py >> ax >> ay >> bx >> by;
    auto comp = [&](double a, double b){
        if(a < b) return true;
        if(fabs(a - b) <= eps) return true;
        return false;
    };
    auto dist = [&](double x1, double y1, double x2, double y2){
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    };
    auto check = [&](double r) -> bool {
        bool f1 = false, f2 = false, f3 = false;
        if (comp(dist(px, py, ax, ay), r * r) && comp(dist(0, 0, ax, ay), r * r)) f1 = true;
        if (comp(dist(px, py, bx, by), r * r) && comp(dist(0, 0, bx, by), r * r)) f1 = true;
        if (comp(dist(ax, ay, bx, by), 4 * r * r)) f1 = true;
        if (comp(dist(px, py, ax, ay), r * r) || comp(dist(px, py, bx, by), r * r)) f2 = true;
        if (comp(dist(0, 0, ax, ay), r * r) || comp(dist(0, 0, bx, by), r * r)) f3 = true;
        return f1 && f2 && f3;
    };
    double l = 0, r = 1e5, mid;
    while(r - l > eps){
        mid = (l + r) / 2;
        if(check(mid)) r = mid;
        else l = mid;
    }
    cout << fixed << setprecision(9) << l << '\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();
}

其实可以直接解,但精度要求不高,浮点数二分过得去

C. Decreasing String

题意

给定一个长为 \(n\) 的由小写字母组成的字符串 \(s\),以及初始状态下等于 \(s\) 的字符串 \(p\)

接下来会有 \(n - 1\) 次操作,每次操作会从字符串里删除一个字符,并满足删除的所有方案中,该方案删除后得到的字符串字典序最小。

对于每次操作完后得到的 \(s\),将其拼接到 \(p\) 的后面,从而最后 \(p\) 是一个长度为 \(\frac{n(n - 1)}{2}\) 的字符串。

给定一个整数 \(x\),输出 \(p_x\) 对应的字符。

思路

首先,删除的方案是:如果存在一个点 \(i\),满足 \(s_i > s_{i + 1}\),那么删除 \(s_i\);否则,我们从最后一个字符开始往前删。

暴力模拟是不可行的,但是考虑到单点查询,我们完全可以不用推出前面操作后得到的答案。

那么我们只要处理查询的字符对应的字符串是什么即可。

我们可以简单计算得到,\(x\) 位于第几个字符串,我们记其为 \(l\)

那么,问题转化为:维护一个长为 \(l\) 的单调序列,如果在长度大于 \(l\) 时已经递增,那么考虑从后往前删。

那么,我们直接用单调栈即可解决此题。合理使用 \(\mathtt{stl}\) 即可。

时间复杂度:\(O(n \log 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 = 2e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

void solve() {
    string s;
    int pos;
    cin >> s >> pos;
    int n = s.size();
    s = " " + s;
    int at = 0, sum = 0;
    for(int i=1;i<=n;i++){
        sum += n - i + 1;
        if(sum >= pos){
            at = i;
            break;
        }
    }
    pos -= ((n - (at - 1) + 1) + n) * (at - 1) / 2;
    set<int> st;
    int ind = 0;
    st.ep(++ ind);
    for(int i=2;i<=at;i++){
        while(ind + 1 <= n && (st.empty() || s[ind + 1] >= s[*st.rbegin()]))
            st.ep(++ ind);
        st.extract(-- (st.end()));
    }
    for(int i=ind+1;i<=n;i++) st.ep(i);
    auto ans = st.begin();
    for(int i=1;i<pos;i++) ans ++;
    cout << s[*ans];
}

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();
}

写完才发现写了个单调栈(x

D. Monocarp and the Set

题意

对于一个序列 \(a\),以及一个空字符串 \(s\),定义构造方式如下:

对于 \(i \in [2, n]\),如果 \(a_i\) 比前面的所有数都小,那么在 \(s\) 里放入一个 \(<\);如果 \(a_i\) 比前面的所有数都大,那么放入 \(>\);否则放入 \(?\)

现在,给定构造好的字符串 \(s\),并给定 \(q\) 个非独立询问:

每个询问给定一个整数 \(i\) 和一个字符 \(c\),并将 \(s_i\) 修改为 \(c\)

输出询问前,以及每一次询问修改后,字符串 \(s\) 对应的原序列 \(a\) 有多少种。

思路

我们考虑逆向思维。

首先,如果当前位置是 \(>\)\(<\),那么这个位置的数值是唯一确定的,否则,如果这个位置位于 \(i\),我们就有 \(i - 1\) 种选择。

为何是 \(i - 1\) ?因为我们只能把最小值或最大值放在第一个位置,而因为这个位置是 \(?\),所以它不可以是可以放置的最大值或最小值,从而会少一种取法。

最后累乘即可。

至于询问,我们直接模拟替换,然后更新答案即可,当然,如果第一位是 \(?\),我们在最后输出的时候输出 \(0\) 即可。

时间复杂度:\(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 = 2e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9, PI = acos(-1);

static __int128 qp(__int128 x, __int128 y, __int128 m = mod) {
    static __int128 ans;
    ans = 1, x %= m;
    for (; y; y >>= 1, x = x * x % m) if (y & 1) ans = ans * x % m;
    return ans;
}

int inv(int n, int m = mod) { return qp(n, m - 2, m); }

void solve() {
    int n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    int ans = 1;
    for(int i=1;i<n;i++){
        if(s[i] == '?') ans = ans * i % mod;
    }
    cout << (s[0] == '?' ? 0 : ans) << '\n';
    while(q --){
        int x;
        char c;
        cin >> x >> c;
        if(x != 1) {
            if (s[x - 1] != '?' && c == '?') {
                ans = ans * (x - 1) % mod;
            } else if (s[x - 1] == '?' && c != '?') {
                ans = ans * inv(x - 1) % mod;
            }
        }
        s[x - 1] = c;
        cout << (s[0] == '?' ? 0 : ans) << '\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();
}

最神秘的是打表可以看出规律(x