Codeforces Round #536 (Div. 2)

本文提供了Codeforces Round #536(Div.2)比赛中的六道题目的详细解析,包括A题的交叉图案计数、B题的餐厅菜品分配、C题的数列分组最小化平方和、D题的字典序最小路径、E题的红包领取策略以及F题的数列求解。

Codeforces Round #536 (Div. 2)

A

题目大意

给你一个\(n\times n(n\leqslant500)\)的矩阵,只包含.X,问最多可以找到多少个\(cross\),一个\(cross\)为如下图形:

X_X
_X_
X_X

_表示可以为任意字符。不同的\(cross\)之间可以重叠

题解

可以枚举中间X,判断是否合法

卡点

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define maxn 510

int Tim, n, m, ans;
char s[maxn][maxn];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%s", s[i] + 1);
    }
    for (int i = 2; i < n; ++i) {
        for (int j = 2; j < n; ++j) if (s[i][j] == 'X') {
            if (s[i - 1][j - 1] == 'X' && s[i + 1][j - 1] == 'X'
                    && s[i - 1][j + 1] == 'X' && s[i + 1][j + 1] == 'X') ++ans;
        }
    }
    printf("%d\n", ans);
    return 0;
}

B

题目大意

\(n(n\leqslant10^5)\)道菜,第\(i\)道菜有\(a_i\)份,每个价值都为\(c_i\)。有\(m(m\leqslant10^5)\)个顾客,第\(i\)个顾客要\(d_i\)份第\(t_i\)种菜(只有第\(i\)个顾客走后第\(i+1\)个顾客才会来)。餐厅按如下规则给菜(一份菜一份菜给):

  1. 若当前有第\(t_i\)种菜,就给该顾客一份
  2. 若没有第\(t_i\)种菜,就给该顾客最便宜的菜,若有多个最便宜的菜,给编号最小的
  3. 若没有菜,顾客会愤怒的离开

若该顾客没有愤怒的离开,输出餐厅给的菜的价值总和,若离开了,输出\(0\)(注意,已经给这个顾客的菜依然给了这个顾客,也就是说无法回收菜)

题解

原来的题目中似乎没有注意部分,导致我以为是线段树上二分,然后不想写,先写了\(C\)\(D\),然后出题人发公告。于是变成了真正的\(B\)题难度。

只需要记录一下价值最小的没有被用完的菜就行了,直接模拟即可,发现一道菜只会被用完一次,所以复杂度是\(O(n+m)\)

卡点

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define maxn 100010

int Tim, n, m, lst;
long long a[maxn], c[maxn];
int ret[maxn], rnk[maxn];
int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
    for (int i = 1; i <= n; ++i) {
        std::cin >> c[i];
        rnk[i] = i;
    }
    std::sort(rnk + 1, rnk + n + 1, [] (int a, int b) { return c[a] < c[b]; });
    for (int i = 1; i <= n; ++i) ret[rnk[i]] = i;
    lst = 1;
    while (m --> 0) {
        long long t, d, ans = 0;
        std::cin >> t >> d;
        if (a[t] >= d) {
            a[t] -= d;
            std::cout << d * c[t] << '\n';
            continue;
        }
        ans = a[t] * c[t];
        d -= a[t];
        a[t] = 0;
        for (int i; lst <= n; ++lst) {
            i = rnk[lst];
            if (a[i] >= d) {
                ans += d * c[i];
                a[i] -= d;
                d = 0;
                std::cout << ans << '\n';
                break;
            }
            ans += a[i] * c[i];
            d -= a[i];
            a[i] = 0;
        }
        if (d) std::cout << "0\n";
    }
    return 0;
}

C

题目大意

\(n(n\leqslant3\times10^5)\)个数,保证\(n\)为偶数,要把它们分成若干组,每组至少两个数。假设分成\(m\)组,第\(i\)组的和为\(s_i\),要求最小化\(\sum\limits_{i=1}^ms_i^2\)

题解

发现一定是每组两个数,因为把两个总和分别为\(a,b\)的组合起来的时候,代价从\(a^2+b^2\)变为了\(a^2+2ab+b^2\)肯定不优秀。继续发现要让\(\max\{s_i\}\)最小,于是排一个序,每次取最小和最大的放为一组即可。

卡点

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define maxn 300010

int Tim, n, m;
int s[maxn];
long long ans;
int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0);
    std::cin >> n;
    for (int i = 0; i < n; ++i) std::cin >> s[i];
    std::sort(s, s + n);
    const int nn = n >> 1;
    for (int i = 0, x; i < nn; ++i) {
        x = s[i] + s[n - i - 1];
        ans += x * x;
    }
    std::cout << ans << std::endl;
    return 0;
}

D

题目大意

给你一张\(n(n\leqslant10^5)\)个点\(m(m\leqslant10^5)\)条边的无向图,现在在点\(1\),每次到一个没有经过过的点就把那个点记录下来,直到经过所有\(n\)个点,问最后记录下的序列最小的字典序是什么,可以重复经过点和边。

题解

这可比\(NOIP2018D2T1\)简单多了,只需要求一个一个类似最小生成树的东西就行了。用一个小根堆记录当前经过过的点可以到达的没有经过过的点,每次取出堆顶,把与它相连的点加入堆即可。

卡点

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define maxn 100010
int head[maxn], cnt;
struct Edge {
    int to, nxt;
} e[maxn << 1];
inline void addedge(int a, int b) {
    e[++cnt] = (Edge) { b, head[a] }; head[a] = cnt;
    e[++cnt] = (Edge) { a, head[b] }; head[b] = cnt;
}

int Tim, n, m;
std::priority_queue<int, std::vector<int>, std::greater<int> > q;
bool inq[maxn];
int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> n >> m;
    for (int i = 0, a, b; i < m; ++i) {
        std::cin >> a >> b;
        addedge(a, b);
    }
    q.push(1);
    inq[1] = true;
    for (int Tim = 1; Tim < n; ++Tim) {
        int u = q.top(); q.pop();
        std::cout << u << ' ';
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (!inq[v]) {
                q.push(v);
                inq[v] = true;
            }
        }
    }
    std::cout << q.top() << std::endl;
    return 0;
}

E

题目大意

\(n(n\leqslant10^5)\)个时间点,分别为\(1\sim n\),有\(k(k\leqslant10^5)\)个红包,第\(i\)个红包可以在\([s_i,t_i]\)内领取,有钱\(w_i\),领了这个红包,直到\(d_i\)时间点之后(不包含\(d_i\))才可以领取下一个红包。

\(Bob\)按如下规则领红包:

  1. 领当前可以领的最大的红包
  2. 若有多个,领\(d\)最大的红包
  3. 若还有多个,随机领一个

\(Alice\)希望\(Bob\)领的最少,她可以在最多\(m(m\leqslant200)\)个时间点打扰\(Bob\),使得他不能领红包,问\(Bob\)最少领到多少钱

题解

(比赛时做\(F\)题,就没有做\(E\),而且\(F\)没在比赛内写出来。。。。)

发现每个时间点\(Bob\)领的红包是一定的,可以预处理出来,用\(set,map\)什么的,注意有重复元素,设第\(i\)个时间点领的红包为\(s_i\)

\(f_{i,j}\)表示打断了\(i\)次,现在在第\(j\)个时间点\(Bob\)领的最少钱数,转移为\(f_{i.j}\to f_{i+1,j+1}\)\(f_{i,j}+w_{s_j}\to f_{i,d_{s_j}}\)

卡点

C++ Code:

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <vector>
#define maxn 100010
typedef std::pair<int, int> PII;
const long long inf = 0x3f3f3f3f3f3f3f3f;
inline void chkmin(long long &a, long long b) { if (a > b) a = b; }

int n, m, k, now = 1, nxt;
long long f[2][maxn], ans = inf;
std::vector<PII> Add[maxn], Del[maxn];
PII Max[maxn];
std::map<PII, int> mp;


int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> n >> m >> k;
    for (int i = 0, s, t, d, w; i < k; ++i) {
        std::cin >> s >> t >> d >> w;
        Add[s].push_back(std::make_pair(w, d));
        Del[t + 1].push_back(std::make_pair(w, d));
    }
    for (int i = 1; i <= n; ++i) {
        for (PII V : Add[i]) {
            if (mp.count(V)) ++mp[V];
            else mp[V] = 1;
        }
        for (PII V : Del[i]) {
            if (!--mp[V]) mp.erase(V);
        }
        if (mp.size()) Max[i] = (*mp.rbegin()).first;
        else Max[i] = std::make_pair(0, i);
    }

    const int SZ = sizeof f[now];
    __builtin_memset(f[nxt], 0x3f, SZ);
    f[nxt][1] = 0;
    for (int i = 0; i <= m; ++i) {
        std::swap(now, nxt);
        __builtin_memset(f[nxt], 0x3f, SZ);
        f[nxt][1] = 0;
        for (int j = 1; j <= n; ++j) {
            chkmin(f[nxt][j + 1], f[now][j]);
            chkmin(f[now][Max[j].second + 1], f[now][j] + Max[j].first);
        }
        chkmin(ans, f[now][n + 1]);
    }
    std::cout << ans << std::endl;
    return 0;
}

F

题目大意

有一串\(n(n\leqslant10^9)\)个数的数列,给你\(b_1\sim b_k(k\leqslant100)\)。当\(i>k\)时:
\[ f_i=(\prod\limits_{i=1}^kf_{i-j}^{b_i})\bmod{998244353} \]
已知\(f_1=f_2=\cdots=f_{k-1}=1,f_n=m\),问最小的正整数\(f_k\)可能是多少

题解

写一下式子,发现可以用矩阵快速幂求出\(f_n=f_k^x\bmod{998244353}\)中的\(x\)(幸好这道题不需要线性齐次递推,不然玩完),复杂度\(O(k^3\log_2n)\)

接下来就是求\(f_k^x\equiv m\pmod{998244353}\)
\[ x\ln(f_k)\equiv\ln(m)\pmod{\varphi(998244353)}\\ \ln(f_k)\equiv\ln(m)x^{-1}\pmod{998244352}\\ f_k\equiv\exp(\ln(m)x^{-1})\pmod{998244353}\\ \]
但是\(x\)\(\pmod{998244352}\)下可能没有逆元,怎么办呢?可以用\(exgcd\)求出\(\dfrac{\gcd(x,998244352)}x\),然后把\(\ln(m)\)除掉\(\gcd(x,998244352)\),若有余数则无解。

卡点

比赛结束后\(5min\)发现可以除掉\(\gcd\)来做,然后自闭

C++ Code:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <map>
#include <set>
const int mod = 998244353, __mod = mod - 1;
#define maxn 105

int Tim, n, m, k;
int b[maxn];
struct Matrix {
    int s[maxn][maxn];
    inline Matrix operator * (const Matrix &rhs) const {
        Matrix res;
        for (int i = 0; i < k; ++i) {
            for (int j = 0; j < k; ++j) {
                long long t = 0;
                for (int l = 0; l < k; ++l) (t += static_cast<long long> (s[i][l]) * rhs.s[l][j]) %= __mod;
                res.s[i][j] = t;
            }
        }
        return res;
    }
} base, res;

namespace Math {
    std::map<int, int> mp;
    bool init = false;
    long long BSGS(long long y, long long z) {
        y %= mod, z %= mod;
        if (!y) return -1;
        long long tmp = 1, t = sqrt(mod - 1) + 1;
        if (!init) {
            mp.clear();
            for (int i = 0; i <= t; i++) {
                mp[tmp * z % mod] = i;
                if (i != t) tmp = tmp * y % mod;
            }
            init = true;
        }
        long long tmp6 = tmp;
        for (int i = 1; i <= t; i++) {
            if (mp.count(tmp6)) return i * t - mp[tmp6];
            tmp6 = tmp6 * tmp % mod;
        }
        return -1;
    }

    inline int pw(int base, int p) {
        static int res;
        for (res = 1; p; p >>= 1, base = static_cast<long long> (base) * base % mod) if (p & 1) res = static_cast<long long> (res) * base % mod;
        return res;
    }

    long long exgcd(long long a, long long b, long long &x, long long &y) {
        if (!b) {
            x = 1, y = 0;
            return a;
        }
        long long t = exgcd(b, a % b, y, x);
        y -= a / b * x;
        return t;
    }
    long long retgcd;
    long long inv(long long a) {
        long long x, y;
        retgcd = exgcd(a, __mod, x, y);
        return (x % __mod + __mod) % __mod;
    }
}

int main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> k;
    for (int i = 0; i < k; ++i) std::cin >> base.s[i][0];
    for (int i = 1; i < k; ++i) base.s[i - 1][i] = 1;
    res.s[0][0] = 1;
    std::cin >> n >> m;
    for (n -= k; n; n >>= 1, base = base * base) if (n & 1) res = res * base;
    const long long y = res.s[0][0];
    long long lnz = Math::BSGS(3, m);
    long long t = Math::inv(y);
    if (lnz % Math::retgcd) {
        puts("-1");
        return 0;
    }
    lnz = lnz / Math::retgcd * t % __mod;
    std::cout << Math::pw(3, lnz) << '\n';
    return 0;
}

转载于:https://www.cnblogs.com/Memory-of-winter/p/10345347.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值