2023 CCPC 湘潭邀请赛题解 更新至 8 题 (2023 Jiangsu Collegiate Programming Contest)

Preface

拖了好久,半个月前的vp,补完题之后一直没有写题解,题的一些细节几乎都快记不起来了,写的有点含糊。

最近打了重庆市赛和南昌,长春邀请赛,感觉要打成sb了。

每次写代码的时候都是正式比赛的时候,其余时间全在赶作业ddl。

认定是打完南昌太自信了,长春打了个稀巴烂,历史最差战绩,自己纯纯战犯。打比赛态度消极+只睡了5个小时脑子不清醒,赛时表现太差。下次比赛一定不能give up,还有赛前晚上一定不能v太晚,vp黑龙江省赛的题到凌晨2点,给我干成了省空间小王子,uint16都试出来了。

焯怎么这周还有考试。

我会在代码一些有必要的地方加上注释,签到题可能一般就不会写了.

以下是代码火车头:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#include <functional>
#include <random>
#include <bitset>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#define endl '\n'
#define int long long
#define rep(i,a,b) for (int aa = a, bb = b, i = aa; i <= bb; i++)
#define rep2(i,a,b) for (int aa = a, bb = b, i = aa; i >= bb; i--)

namespace pbds = __gnu_pbds;

using namespace std;

template<class Key>
using pbds_set = pbds::tree<Key, pbds::null_type, std::less<Key>,
    pbds::rb_tree_tag, pbds::tree_order_statistics_node_update>;

template<class T>
using pbds_queue = pbds::priority_queue<T>;

template<typename T>
using vec = vector<T>;

using PII = pair<int, int>;

using INT = __int128;

template<typename T>
void cc(const vector<T>& tem) {
    for (const auto& x : tem) cout << x << ' ';
    cout << endl;
}

template<typename... Args>
void cc(const Args &... a) {
    size_t idx = 0;
    ((std::cout << a << (++idx == sizeof...(Args) ? "\n" : " ")), ...);
}

void cc() { cout << endl; }

void fileRead() {
#ifdef LOCALL
    freopen("D:\\AADVISE\\cppvscode\\CODE\\in.txt", "r", stdin);
    freopen("D:\\AADVISE\\cppvscode\\CODE\\out.txt", "w", stdout);
#endif
}

void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }

inline int max(int a, int b) {
    if (a < b) return b;
    return a;
}

inline double max(double a, double b) {
    if (a < b) return b;
    return a;
}

inline int min(int a, int b) {
    if (a < b) return a;
    return b;
}

inline double min(double a, double b) {
    if (a < b) return a;
    return b;
}

void cmax(int& a, const int& b) { if (b > a) a = b; }
void cmin(int& a, const int& b) { if (b < a) a = b; }
void cmin(double& a, const double& b) { if (b < a) a = b; }
void cmax(double& a, const double& b) { if (b > a) a = b; }

int gcd(int a, int b) {
    if (!b) return a;
    return gcd(b, a % b);
}

template<const int T>
int Kuai(int a, int b) {
    int l = 1;
    while (b) {
        if (b % 2) l = l * a % T;
        a = a * a % T, b /= 2;
    }
    return l;
}

int getb(int i, int j) { return i >> j & 1; }

Problem A. Today's Word

首先可以知道,当这个操作的次数比较多的时候,显然最后那一段长度为m的那一段,每一个字符都会进行next操作,而每26次就是一个循环,这个时候我们可以直接得出来答案了。

大概的计算一下,10的100次方对26取模是16,也就是说我们其实操作16次之后就会进入一个循环了。

所以这16次我们可以直接模拟做,但是有一个问题是,16次操作之后不一定就会大于m,所以我们需要16+26次,然后直接输出答案就好了。


//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------
bool fl = 0;
string dfs(const string& s) {

    string ans;

    if (fl == 1 or (s.size() / 4 >= m)) {
        fl = 1;
        rep(i, s.size() - m, s.size() - 1) {
            if (s[i] < 'z')
                ans.push_back((char)(s[i] + 1));
            else ans.push_back('a');
        }
        return ans;
    }
    ans = s.substr(0, s.size() / 2) + s;
    rep(i, 0 + s.size() / 2, s.size() - 1) {
        if (s[i] < 'z')
            ans.push_back((char)(s[i] + 1));
        else ans.push_back('a');
    }
    return ans;
}

signed main() {
    fileRead();
    kuaidu();
    T = 1;
    // cin >> T;
    while (T--) {
        cin >> n >> m;
        string s; cin >> s;


        rep(i, 1, 26 + 16) {
            s = dfs(s);
        }
        rep(i, s.size() - m, s.size() - 1) cout << s[i];
    }
    return 0;
}
/*
4
aaaa
aaaa

17
 sofiawilumerrymee
 sofiawillumarryme

32
takethesebrokenwingandlearntofly
takethesesunkeneyesandlearntosee

*/




Problem E. LCM Plus GCD

首先这个需要一个二进制枚举的容斥的前置知识,大概是这样的:

我们假设容斥的元素是2个A,B,那么一般会有A+B再去掉A和B的交集。

假设容斥的元素是3个,A,B,C,那么就是A+B+C-AB-AC-BC+ABC

如果我们的元素是n个,我们可以用(1<<n)-1,二进制的形式去描述,1代表当前这个元素参与,0代表不参与,然后这个+和-如何判断呢?其实我们会发现,如果参与的元素的个数是奇数,那么就是+,如果是偶数就是-。所以我们可以二进制枚举,然后通过判断有多少个1代表当前操作的贡献我们是加上还是减去。

那么这个题和容斥的关系在哪里呢?

首先我们假设lcm是gcd的y倍,gcd的值是a,那么a+ay=x。

我们可以枚举出来x的因数,a(y+1)=x,一个因数为a,另一个因数就是y+1。

那现在我们需要保证lcm是gcd的y倍,我们可以对y这个数字进行质因数分解,拆成\(2^{a1}*3^{a2}*5^{a3}*...\)的形式,然后我们需要保证,这些质因数分配到k个数字上去(也就是说我们要把a这个数字分配到k个数字上去,每一个数字分配的范围都是0到a之间),不能有相同的,然后这些a1,a2等必须起码有一个数字分配给这k个数字里面的其中一个分配的是0,(也就是a没有给这一个数字分配,这样才能够保证gcd还是a,否则gcd就会是a的倍数)。另外,还需要保证分配完了之后,a会被分配到起码有一个数字对应的值是a。(就是说有一个数字分配的值是a,要满足每一个a都是这样的),这一步就是为了满足lcm确实是y的倍数,否则分配后实际的lcm会比lcm小,因为a没有分配完。

然后容斥的内容大概是这样的:

我们的dfs函数是做一个容斥,但是有限制是gcd等于1.

我们可以假设一个完整全面的情况是gcd为1的倍数,而我们希望gcd为1,那么就需要用这个gcd为1的倍数去减去gcd是2,3,5等的倍数,但是由于会减去重复的,所以我们要再加上是6的倍数,10的倍数,之类的,但是又因为重复了,所以又会进行减去之类的操作,这样反反复复。

int dfs(vec<int>& tem) {
    int len = tem.size();
    int lim = (1ll << len) - 1;
    int ans = 0;
    rep(i, 0, lim) {
        vec<int> tem2(tem);
        bool ad = 1;
        rep(j, 0, len - 1) {
            if (getb(i, j)) {
                ad = 1 - ad;
                tem2[j] -= 1;
            }
        }
        if (ad) ans += dfs2(tem2);
        else ans -= dfs2(tem2);
        ans %= mod, ans += mod, ans %= mod;
        // cc(ans);
    }
    return ans;
}

例如在这段代码中,我们设置二进制里1代表是这个数的倍数,不考虑0的意义。

那么一开始全是0的时候,算出来的贡献是gcd是1的倍数的贡献,然后有一个位置是1的时候,就代表要减去gcd是这个数的倍数的贡献,如果有两个1,就代表要把gcd是这个数字的倍数的贡献加回来。

以上代码的内容的含义最后计算出来的结果就是gcd为1的时候的贡献的值,通过容斥将除了1以外的那些倍数都减去了。

而lcm是否等于y的判断在下面代码的dfs2的内容中,意思也是大同小异,只不过这里的内容是去减掉lcm是这个数的因数的贡献,例如加上lcm是这个数以及因数的贡献,然后减去lcm/2的这个数以及因数的贡献,减去lcm/3的这个数的以及因数的贡献,再加上...

具体的内容见代码:

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:
class FENJIE {
    int mul(int a, int b, int m) {
        return static_cast<__int128>(a) * b % m;
    }

    int power(int a, int b, int m) {
        int res = 1 % m;
        for (; b; b >>= 1, a = mul(a, a, m))
            if (b & 1) res = mul(res, a, m);
        return res;
    }

    vector<int> ddd(vector<PII> tem) {
        vector<int> p;
        int l = 1;
        function<void(int)> f = [&](int n) {
            if (n == tem.size()) {
                p.push_back(l);
                return;
            }
            if (tem[n].second) {
                tem[n].second -= 1;
                l *= tem[n].first;
                f(n);
                l /= tem[n].first;
                tem[n].second += 1;
            }
            f(n + 1);
            };
        f(0);
        sort(p.begin(), p.end());
        return p;
    }

public:
    //求出一个数所有的质因数
    vector<int> factorize(int n) {
        vector<int> p;
        function<void(int)> f = [&](int n) {
            if (n <= 10000) {
                for (int i = 2; i * i <= n; ++i)
                    for (; n % i == 0; n /= i)
                        p.push_back(i);
                if (n > 1) p.push_back(n);
                return;
            }
            if (isprime(n)) {
                p.push_back(n);
                return;
            }
            auto g = [&](int x) { return (mul(x, x, n) + 1) % n; };
            int x0 = 2;
            while (true) {
                int x = x0, y = x0, d = 1, power = 1, lam = 0, v = 1;
                while (d == 1) {
                    y = g(y), ++lam, v = mul(v, abs(x - y), n);
                    if (lam % 127 == 0) { d = gcd(v, n), v = 1; }
                    if (power == lam) { x = y, power *= 2, lam = 0, d = gcd(v, n), v = 1; }
                }
                if (d != n) {
                    f(d), f(n / d);
                    return;
                }
                ++x0;
            }
            };
        f(n);
        sort(p.begin(), p.end());
        return p;
    }

    //判断一个数是不是质数
    bool isprime(int n) {
        if (n < 2) return false;
        static constexpr int A[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
        int s = __builtin_ctzll(n - 1);
        int d = (n - 1) >> s;
        for (auto a : A) {
            if (a == n) return true;
            int x = power(a, d, n);
            if (x == 1 || x == n - 1) continue;
            bool ok = false;
            for (int i = 0; i < s - 1; ++i) {
                x = mul(x, x, n);
                if (x == n - 1) {
                    ok = true;
                    break;
                }
            }
            if (!ok) return false;
        }
        return true;
    }

    //求出一个数的质因数,PII形式,代表质因数和指数
    vector<PII> zhiyinshu(int n) {
        vector<int> tem = factorize(n);
        vector<PII> tmp;
        tem.push_back(-1);
        int las = 0, cnt = 0;
        for (auto x : tem) {
            if (x != las) {
                if (las)tmp.push_back({ las, cnt });
                cnt = 1, las = x;
            }
            else cnt++;
        }
        return tmp;
    }

    //求出一个数的所有的因数
    vector<int> factor(int n) { return ddd(zhiyinshu(n)); }
} fj;

namespace nn {
    const int N = 1e5 + 10;
    vector<int> fact, infact;

    void clear() {
        fact.resize(N + 5);
        infact.resize(N + 5);
        fact[0] = infact[0] = 1;
        for (int i = 1; i < N; i++)  fact[i] = fact[i - 1] * i % mod;
        infact[N - 1] = Kuai<mod>(fact[N - 1], mod - 2);
        for (int i = N - 2; i >= 1; i--) {
            infact[i] = infact[i + 1] * (i + 1) % mod;
        }
    }

    int C(int n, int m) {
        if (n < m or m < 0) return 0;
        return fact[n] * infact[m] % mod * infact[n - m] % mod;
    }

}

//--------------------------------------------------------------------------------

int k;

int dfs2(vec<int>& tem) {
    int len = tem.size();
    int lim = (1ll << len) - 1;
    int ans = 0;
    rep(i, 0, lim) {
        bool ad = 1;
        int val = 1;
        rep(j, 0, len - 1) {
            if (getb(i, j)) {
                ad = 1 - ad;
                val *= tem[j];
                val %= mod;
            }
            else {
                val *= tem[j] + 1;
                val %= mod;
            }
        }
        if (ad) ans += nn::C(val, k);
        else ans -= nn::C(val, k);
        // cc(ans);
        ans %= mod, ans += mod, ans %= mod;
    }
    return ans;
}

int dfs(vec<int>& tem) {
    int len = tem.size();
    int lim = (1ll << len) - 1;
    int ans = 0;
    rep(i, 0, lim) {
        vec<int> tem2(tem);
        bool ad = 1;
        rep(j, 0, len - 1) {
            if (getb(i, j)) {
                ad = 1 - ad;
                tem2[j] -= 1;
            }
        }
        if (ad) ans += dfs2(tem2);
        else ans -= dfs2(tem2);
        ans %= mod, ans += mod, ans %= mod;
        // cc(ans);
    }
    return ans;
}
signed main() {
    fileRead();
    kuaidu();

    nn::clear();

    T = 1;
    //cin >> T;
    while (T--) {
        cin >> n >> k;
        vec<int> tem = fj.factor(n);
        int ans = 0;

        for (auto& a : tem) {
            int y = n / a - 1;
            if (y <= 1) continue;

            vec<PII> zys1 = fj.zhiyinshu(y);
            vec<int> zys;
            for (auto& [a, b] : zys1) {
                zys.push_back(b);
            }
            // cc(y);
            // cc(zys);
            ans += dfs(zys);
            ans %= mod;
        }
        cc(ans);
    }
    return 0;
}

Problem F. Timaeus

一个简单dp,但是一开始想的不太对,因为题干要求两个人里面只能选择一个,所以如果用递推的那种形式,我们无法保证这个选择只会选择一个,所以用这个记忆化递归的这种形式。

但是式子写出来之后需要特判一下B是1的情况,否则状态转移会转移到自己的身上。

特判的情况队友张神直接给了式子,我直接傻乎乎腾上去。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:
double dp[M];
bitset<M> fl;
double p, q;
int A, B;
//--------------------------------------------------------------------------------

double dfs(int x) {
    if (x < 0) return -INF;
    if (fl[x]) return dp[x];
    if (x == 0) return 0;
    fl[x] = 1;
    cmax(dp[x], p * (dfs(x - B) + 2) + (1 - p) * (dfs(x - B) + 1));
    cmax(dp[x], q * (dfs(x - B + 1) + 1) + (1 - q) * (dfs(x - B) + 1));
    return dp[x];
}

signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        int  p1, q1;
        cin >> A >> B >> p1 >> q1;
        p = p1 * 1.0 / 100;
        q = q1 * 1.0 / 100;

        if (B == 1) {
            double q1 = (p * 2) + (1 - p);
            double q2 = (1.0) / (1 - q);
            double ans = max(q1, q2) * A;
            cc(ans);

            continue;
        }

        double ans = dfs(A);

        cout << fixed << setprecision(10) << ans << endl;
        // continue;

    }
    return 0;
}





Problem H. Neil's Machine

我也忘了具体细节了,貌似是个签到模拟题?

//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------
int A[N];
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    // cin >> T;
    while (T--) {
        cin >> n;
        string s, t;
        cin >> s >> t;
        rep(i, 0, n - 1) {
            A[i] = s[i] - t[i] + 26;
            A[i] %= 26;
        }
        int st = 0;
        while (st < n and A[st] == 0) st++;
        if (st >= n) {
            cc(0);
            continue;
        }
        set<int> S;
        int las = 0;
        int ans = 0;
        rep(i, st, n - 1) {
            int ed = i + 1;
            while (A[ed] == A[ed - 1] and ed < n) ed++;
            ed -= 1;
            int tem = (A[i] + las) % 26;
            // if (tem != 0) ans++;
            ans++;
            las += A[i];
            i = ed;
        }
        cc(ans);
    }
    return 0;
}
/*
4
aaaa
aaaa

17
 sofiawilumerrymee
 sofiawillumarryme

32
takethesebrokenwingandlearntofly
takethesesunkeneyesandlearntosee

*/




Problem I. Elevator

纯纯签到,直接输出n-m+1

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------

signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        if (n == m) {
            cc(1);
            continue;
        }
        cc(n - m + 1);
    }
    return 0;
}





Problem J. Similarity (Easy Version)

直接模拟他说的这个

//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------
string A[N];

int dfs(const string& s1, const string& s2) {
    int ans = 0;
    int l1 = s1.size(), l2 = s2.size();
    rep(i, 0, l1 - 1) {
        rep(j, 0, l2 - 1) {
            int tem = 0;
            rep(p, 0, 100) {
                if ((i + p) >= l1 or (j + p) >= l2) break;
                if (s1[i + p] == s2[j + p]) tem++;
                else break;
            }
            cmax(ans, tem);
        }
    }
    return ans;
}

signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n;
        rep(i, 1, n) {
            cin >> A[i];
        }
        int ans = 0;
        rep(i, 1, n) {
            rep(j, i + 1, n) {
                cmax(ans, dfs(A[i], A[j]));
            }
        }
        cc(ans);
    }
    return 0;
}
/*
4
aaaa
aaaa

17
 sofiawilumerrymee
 sofiawillumarryme

32
takethesebrokenwingandlearntofly
takethesesunkeneyesandlearntosee

*/




Problem K. Similarity (Hard Version)

一个sb构造题,好久没做过构造了,感觉脑子已经秀逗了。

我们可以构造aaabaaabaaa这样的形式,每m个a,然后搭配一个b这样的形式。

然后下一个字符串就构造成aaacaaacaaa这种形式,然后到z的之后a可以变成b,然后还是接着上面的套娃。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;
int k;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------

string dfs(char a, char b) {
    string s;
    rep(i, 1, k) {
        if (i % (m + 1) == 0) {
            s.push_back(b);
        }
        else {
            s.push_back(a);
        }
    }
    return s;
}

signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        cin >> n >> m >> k;

        if (m >= k) {
            cc("No");
            continue;
        }



        if (m == 0) {
            if (n <= 26) {
                int tot = 0;
                cc("Yes");
                rep(i, 1, n) {
                    rep(j, 1, k) {
                        char x = 'a' + i - 1;
                        cout << x;
                    }
                    cc();
                }

            }
            else {
                cc("No");
            }
        }
        else {
            cc("Yes");
            int cnt = 0;
            rep(i, 0, 25) {
                rep(j, i + 1, 25) {
                    cnt++;
                    cc(dfs('a' + i, 'a' + j));
                    if (cnt >= n) goto Z;
                }
            }
        Z:;
        }


    }
    return 0;
}





Problem L. Architect

直接做一个三维差分。要想起来一个有用的trick是查分之后所有的数字应该得是0才可以,否则就说明空间里有非0的存在,那么就一定不行。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
//const int mod = 998244353;
const int INF = 1e16;
auto kuai = Kuai<mod>;
int n, m, T;

//--------------------------------------------------------------------------------
//struct or namespace:
struct node {
    int x, y, z;
    bool operator<(const node& q1) const {
        if (q1.x != x) return x < q1.x;
        if (q1.y != y) return y < q1.y;
        return z < q1.z;
    }
};
map<node, int> mp;

//--------------------------------------------------------------------------------
void dfs(int x1, int y1, int z1, int x2, int y2, int z2, int fl) {
    mp[{x1, y1, z1}] += fl;
    mp[{x2 + 1, y1, z1}] -= fl;
    mp[{x1, y2 + 1, z1}] -= fl;
    mp[{x1, y1, z2 + 1}] -= fl;

    mp[{x2 + 1, y2 + 1, z1}] += fl;
    mp[{x1, y2 + 1, z2 + 1}] += fl;
    mp[{x2 + 1, y1, z2 + 1}] += fl;
    mp[{x2 + 1, y2 + 1, z2 + 1}] -= fl;
}
int A[20][20][20];
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        int w, h, l; cin >> w >> h >> l;
        cin >> n;
        mp.clear();
        rep(i, 1, n) {
            int x1, y1, z1, x2, y2, z2;
            cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
            dfs(x1, y1, z1, x2 - 1, y2 - 1, z2 - 1, 1);
        }
        dfs(0, 0, 0, w - 1, h - 1, l - 1, -1);
        bool flag = 1;

        for (auto [a, b] : mp) {
            // cc(b);
            if (b != 0) {
                flag = 0;
                break;
            }
            // A[a.x][a.y][a.z] = b;
        }

        if (flag) cc("Yes");
        else cc("No");

    }
    return 0;
}





PostScript

能做的事情就是祈求郑州可以发挥好一些

posted @ 2025-05-27 15:27  AdviseDY  阅读(158)  评论(0)    收藏  举报