2024 (ICPC) Jiangxi Provincial Contest 个人vp记录 及 题解 (10 / 12) (江西省赛)

题目链接
题目旁边的数字是纯纯的主观评分(

A.Maliang Learning Painting (800)#

输出 a+b+c

NAME MeIoN_is_UMP45(){
    std::cin >> a >> b >> c;
    std::cout << a + b + c << "\n";
}

B.Magic Leeks (1900)#

显然当 t>=n2 时, 初始韭菜一定会被噶完, 最优策略一定是跑到最左或最右边, 然后用n次移动将全图扫一遍获取最多的增长量, 可以直接模拟一遍

如果 t<n2 , 可以枚举最左或最右端点, 得到可以被扫一遍的每一个区间, 对于每个区间可以使用 前缀和 和 等差数列求和 来算出答案, 需要推一点点式子

NAME MeIoN_is_UMP45(){
    ll p, n;
    std::cin >> n >> p;
    vector<ll> a(n + 2);
    for (int i = 1, ed = n + 1; i < ed; ++i) {
        std::cin >> a[i];
    }
    vector<ll> v(n + 2);
    ll k, t, ans = 0;
    std::cin >> k >> t;
    if (t < n * 2) {
        F(i, n) v[i] = a[i], v[i] += v[i - 1];
        meion get = [&] (int l, int r) { iroha v[r] - v[l - 1]; };
        ll tmp;
        fo (l, std::max(1ll, p - t + 1), p) {
            ll r = l + t - (p - l + 1);
            MIN(r, n);
            MAX(r, p);
            ll len = r - l + 1;
            if (r - l + 1 >= t)
                tmp = (r - l + 1) * (r - l + 2) / 2 * k;
            else 
                tmp = t * (t + 1) / 2 * k - (t - len + 1) * (t - len) / 2 * k;
            MAX(ans, tmp + get(l, r));
        }
        fo (r, p, std::min(n, p + t - 1)) {
            ll l = r - (t - (r - p + 1));
            MAX(l, 1ll);
            MIN(l, p);
            ll len = r - l + 1;
            if (r - l + 1 >= t)
                tmp = (r - l + 1) * (r - l + 2) / 2 * k;
            else 
                tmp = t * (t + 1) / 2 * k - (t - len + 1) * (t - len) / 2 * k;
            MAX(ans, tmp + get(l, r));
        }
    } else {
        f(i, 2){
            ll anss = 0, g = 0;
            F(i, n) v[i] = a[i];
            ll pla = p, time = t;
            while (pla != 1) {
                g += k;
                anss += v[pla] + g;
                v[pla] = -g;
                --pla;
                --time;
            }
            g += (ll)(time - n) * k;
            F(i, n) {
                g += k;
                anss += v[i] + g;
            }
            if (anss > ans) ans = anss;
            std::ranges::reverse(a);
            p = n - p + 1;
        }
    }
    std::cout << ans << "\n";
}

C.Liar (900)#

当有一个人撒谎时, 他的数字可以为 si=1n1a[i] , 所以 ans 至少为 n1
i=1na[i]=s , 此时 ans 可以为 n

NAME MeIoN_is_UMP45(){
    int n;
    ll sum = 0, s;
    std::cin >> n >> s;
    vector<ll> a(n);
    for (meion &x : a) {
        std::cin >> x;
    }
    sum = std::accumulate(a.begin(), a.end(), 0ll);
    if (s == sum) {
        iroha std::cout << n << "\n", void();
    } else {
        iroha std::cout << n - 1 << "\n", void();
    }
}

D.Magic LCM (2000)#

随便举个例子, 有两个数 ab , 设他们互质, 则可以把所有因数转移到其中一个上, 也就是把 ab
变成 1ab.

还是有两个数 ab , 设 他们有一个公共的质因子 c , a=k1c2 , b=k2c5, 则可以交换 c2
c5 使 a=k1c5 , b=k2c2

这样答案就很简单了, 把数组中所有数字进行质因数分解, 记录下每一个质因子在每一个数中出现的次数, 然后进行贪心, 将尽可能多的, 次数尽可能大的质因子堆到一个数上, 直到把所有质因子用完, 剩下的数全是1, 加起来就是ans了

vector<int> minp, primes;
void sieve(int n) {
    minp.assign(n + 1, 0);
    primes.clear();
    for (int i = 2; i <= n; i++) {
        if (minp[i] == 0) {
            minp[i] = i;
            primes.push_back(i);
        }
        for (auto p : primes) {
            if (i * p > n) {
                break;
            }
            minp[i * p] = p;
            if (p == minp[i]) {
                break;
            }
        }
    }
}

int cnt[1000000 + 10];  // 用于记录每个a[i]的质因子出现次数
vector<int> vv[1000000 + 10];   // 用于记录每个质因子每次出现的次数(幂数)
NAME MeIoN_is_UMP45(){
    std::cin >> n;
    vector<int> a(n);
    for (meion &x : a) {
        std::cin >> x;
    }
    ans = n;
    // sse 与 se 仅用于记录质因子以及去重;
    vector<int> sse;
    for (meion &i : a) {
        vector<int> se;
        while (i != minp[i] and i != 1) {
            cnt[minp[i]]++;
            se.emplace_back(minp[i]);
            i /= minp[i];
        }
        se.emplace_back(i);
        cnt[i]++;
        std::ranges::sort(se);
        se.erase(std::unique(se.begin(), se.end()), se.end());
        fom (i, se) {
            vv[i].emplace_back(cnt[i]);
            cnt[i] = 0;
            sse.emplace_back(i);
        }
    }
    std::ranges::sort(sse);
    sse.erase(std::unique(sse.begin(), sse.end()), sse.end());
    for (meion &i : sse) {
        std::ranges::sort(vv[i]);
    }
    set<int> se{ sse.begin(), sse.end() };
    while (se.size()) {
        --ans;
        vector<int> del;
        ll res(1);
        fom (i, se) {
            res = res * ksm(i, vv[i].back()) % mod;
            vv[i].pop_back();
            if (not vv[i].size()) {
                del.emplace_back(i);
            }
        }
        ans += res;
        ans %= mod;
        fom (i, del) {
            se.erase(i);
        }
    }
    ans = (ans + mod) % mod;
    std::cout << ans << "\n";
}

E.Magic Subsequence#

我不会

F.The Ropeways#

我也不会

G.Multiples of 5 (1000)#

给一个 11 进制数字, 问是否是 5 的倍数.

若这个数的第 n 位为 x , 则这一位数字表示的是 x11n1 , 显然这个数的个位还是 x ,
问题询问是否是 5 的倍数, 将所有位对个位的贡献加起来 mod5 , 为 0 的话就是 5 的倍数;

NAME MeIoN_is_UMP45(){
    std::cin >> n;
    char c;
    ll res = 0;
    while (n--) {
        std::cin >> m >> c;
        if (c == 'A') {
            k = 10;
        } else {
            k = c - '0';
        }
        res = (res + m * k) % mod;
    }
    std::cout << (not(res % 5)) ? "Yes\n" : "No\n";
}

H.Convolution (1200)#

通过观察式子, 或者观察一下样例, 可以发现 卷积核中的每一个元素都要和大矩阵的一个子矩阵的每个元素相乘,
然后计入贡献, 并且卷积核中只有 1,0,1 三个数, 所以贡献就是每个子矩阵的元素和的绝对值.

用二维前缀和快速求出贡献

NAME MeIoN_is_UMP45(){
    std::cin >> n >> m >> k >> l;
    vector<vector<int>> a(n + 2, vector<int>(m + 2)), v(k + 2, vector<int>(l + 2));
    F(i, n) F(k, m) {
        std::cin >> a[i][k];
    }
    F(i, n) F(k, m) {
        a[i][k] += a[i][k - 1];
    }
    F(k, m) F(i, n) {
        a[i][k] += a[i - 1][k];
    }
    ans = 0;
    F(x, k) F(y, l) {
        ans += std::abs(a[n + x - k][m + y - l] + a[x - 1][y - 1] - a[n + x - k][y - 1] - a[x - 1][m + y - l]);
    }
    std::cout << ans << "\n";
}

I.Neuvillette Circling (1900)#

显然最小半径一定是一个两点为直径的圆 或者 一个 三个点组成的三角形的外接圆 的半径
所以直接枚举就好了, 外接圆半径求法见 代码

NAME MeIoN_is_UMP45(){
    ld INF = std::numeric_limits<ld>::max();
    std::cin >> n;
    vector<point<ld>> a(n);
    for (meion &p : a) {
        std::cin >> p;
    }
    // 覆盖了 i 条路径的最小半径
    vector<ld> v(n * n / 2 + 1, INF);

    // 两点确定一个圆的情况
    for (int i = 0, ed = n - 1; i < ed; ++i) {
        for (int k = i + 1, ed = n; k < ed; ++k) {
            int sum = 2;
            ld dis = square(a[i] - a[k]);
            for (int j = 0, ed = n; j < n; ++j) {
                if (j == i or j == k) continue;
                sum += square(a[i] - a[j]) + square(a[k] - a[j]) <= dis + eps;
            }
            v[sum * (sum - 1) >> 1] = std::min(v[sum * (sum - 1) >> 1], (ld)std::sqrt(dis) / 2);
        }
    }
    // 三点确定一个圆的情况
    for (int i = 0, ed = n - 2; i < ed; ++i) {
        for (int k = i + 1, ed = n - 1; k < ed; ++k) {
            for (int j = k + 1, ed = n; j < ed; ++j) {
                int sum = 3;
                // 两条边的中点
                point<ld> O, aa = (a[i] + a[k]) / 2.L, bb = (a[i] + a[j]) / 2.L;
                // 中垂线向量
                point<ld> A = rotate(a[k] - a[i]), B = rotate(a[j] - a[i]);
                // 中垂线交点为外接圆圆心
                O = line_x_line(line(aa, A + aa), line(bb, B + bb));
                ld R = length(O - a[i]);
                for (int p = 0, ed = n; p < ed; ++p) {
                    if (p == i or p == j or p == k) continue;
                    sum += length(O - a[p]) <= R + eps;
                }
                v[sum * (sum - 1) >> 1] = std::min(v[sum * (sum - 1) >> 1], R);
            }
        }
    }
    for (int i = n * (n - 1) >> 1, ed = 0; i > ed; --i) {
        v[i] = std::min(v[i - 1], v[i]);
    }
    for (int i = 1, ed_i = (n * (n - 1) >> 1); i <= ed_i; ++i) {
        std::cout << v[i] << "\n";
    }
}

J.Magic Mahjong (1100)#

很简单的模拟题, 根据题意模拟判断几种情况是否成立即可;

NAME MeIoN_is_UMP45(){
    std::cin >> s;
    vector<P<char, int>> v, cc { {'p', 1} , {'p', 9}, {'s', 1}, {'s', 9}, {'m', 1}, {'m', 9} };
    for (int i = 0, ed_i = (s.size()); i < ed_i; i += 2) {
        v.emp(s[i + 1], int(s[i] - '0'));
    }
    Sort(v);
    set<P<char, int>> se;
    for (meion &i : v) {
        se.insert(i);
    }
    meion ck = [&] ()->bool {
        for (int i = 1; i <= 7; ++i) {
            if (not (se.count({'z', i}))) {
                iroha false;
            }
        }
        for (meion &i : cc) {
            if (not (se.count(i))) {
                iroha false;
            }
        }
        iroha true;
    };
    if (ck()) {
        iroha std::cout << "Thirteen Orphans\n", void();
    }
    se.clear();
    for (int i = 0, p = 0, ed_i = (7); i < ed_i; ++i, p += 2) {
        if (v[p] == v[p + 1]) se.insert(v[p]);
    }
    if (se.size() == 7) {
        iroha std::cout << "7 Pairs\n", void();
    }
    iroha std::cout << "Otherwise\n", void();
}

K.Magic Tree (1000)#

显然有 n1 种分岔的情况, 所以答案是 2n1

NAME MeIoN_is_UMP45(){
    std::cin >> n;
    std::cout << ksm(2, n - 1);
}

L.Campus (2200)#

总共 k 个出口 (k<=15), 所以最多有 2k 个出口互不相同的时间段, 可以每个时间段跑一次最短路, 记录每段时间的答案;

最短路的跑法:

建立一个边权为 0 的点连向每个开放的点, 对该点每个时段跑一次dij最短路; 也可以对每一个出口跑一次dij, 每个时段对每个启用的出口的最短路结果取最优.

struct takina{
    ll dis,n;
    bool operator>(const takina &a)const{return dis>a.dis;}
};
int n, m, k, t;
NAME MeIoN_is_UMP45(){
    std::cin >> n >> m >> k >> t;
    ve<int> a(n);
    for (meion &x : a) {
        std::cin >> x;
    }
    ve<int> se{ 1, t + 1 }, L(n), R(n);
    ve<A<int, 3>> pos(k);
    for (meion &[ id, l, r ] : pos) {
        IN(id, l, r), --id;
        se.emp(l), se.emp(r + 1);
        L[id] = l, R[id] = r;
    }
    std::ranges::sort(se);
    se.erase(std::unique(se.begin(), se.end()), se.end());

    ve<ve<P<int, ll>>> v(n + 1);
    ve<A<int, 3>> e;
    for (int i = 0, ed = m; i < ed; ++i) {
        int l, r, w;
        IN(l, r, w), --l, --r;
        v[l].emp(r, w), v[r].emp(l, w);
    }

    ve<ll> dis(n + 1);
    ve<char> vis(n + 1);
    meion dij = [&](int n) {
        for (meion &i : dis) i = INT;
        for (meion &i : vis) i = 0;
        dis[n] = 0;
        std::priority_queue<takina, std::vector<takina>, std::greater<takina>> q;
        q.push({ 0, n });
        while (q.size()) {
            int n = q.top().n;
            q.pop();
            if (vis[n]) continue;
            vis[n] = true;
            for (meion & [ i, val ] : v[n]) {
                if (dis[i] > dis[n] + val) {
                    dis[i] = dis[n] + val;
                    q.push({ dis[i], i });
                }
            }
        }
    };
    
    ve<ll> res(t + 1);
    for (meion ne = se.begin(); meion &t : se) {
        if (t > ::t) break;
        ++ne;
        int ed = std::min(::t + 1, *ne);
        ve<int> del;
        for (meion &[ id, l, r ] : pos) {
            if (l <= t and r >= t) {
                v[n].emp(id, 0ll);
            }
        }
        dij(n);
        ll ans = 0;
        for (int i = 0, ed = n; i < ed; ++i) {
            if (dis[i] != INT) {
                ans += (ll)a[i] * dis[i];
            } else {
                ans = -1;
                break;
            }
        }
        for (; t < ed; ++t) {
            res[t] = ans;
        }
        v[n].clear();
    }
    for (int i = 1, ed = t; i <= ed; ++i) {
        std::cout << res[i] << "\n";
    }
}
posted @   guiding-star  阅读(1242)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
主题色彩