题解 | 牛客周赛 Round 135

A Add Three

模拟和直接计算均可。

模拟:

 void solve() {
     int n;
     cin >> n;
     int p = 0;
     while (p + 3 <= n) p += 3;
     cout << p << endl;
 }

直接计算:

 void solve() {
     int n;
     cin >> n;
     cout << n / 3 * 3 << endl;
 }

B Maximize The Count

$$a_i-a_j=p_i-p_j\Rightarrow a_i-p_i = a_j-p_j$$

记录一下每个数字值与下标之差,取最大即可。

 void solve() {
     int n, mx = 0;
     cin >> n;
     unordered_map<int, int> c;
     for (int i = 1, x; i <= n; ++i) cin >> x, c[x - i]++;
     for (auto [u, v] : c) mx = max(mx, v);
     cout << mx << endl;
 }

C Permutation Swapping

不难发现当 $n\geq 4$ 的时候可以选择任意 $k\neq i,k\neq i+1$ ,进行 $(i\ k)(i+1\ k)(i\ k) = (i\ i+1)$ 的置换,因此必然有解。

剩下的情况特判一下相等即可,$n=3$ 的时候还需要特判一下是否是逆序。

 void solve() {
     int n;
     cin >> n;
     bool f = 1, nf = 1;
     for (int i = 1, x; i <= n; ++i) {
         cin >> x;
         if (x != i) f = 0;
         if (x != n - i + 1) nf = 0;
    }
     if (f || n >= 4 || n == 3 && nf)
         cout << "YES" << endl;
     else
         cout << "NO" << endl;
 }

D Two Options

由于两个操作对于总值的影响分别为 $+1$ 和 $+0$ ,因此最终的和必然大于等于初始的 $sum$ ,那么最优解显然是取最终值为 $p = \lceil\frac{sum}{n}\rceil$ ,又因为必然大于等于,所以减少的次数一定小于增加的次数,于是我们可以把所有减少的次数都与增加的次数绑组,贪心修改即可。本题需要注意负数的上取整处理。

 void solve() {
     int n;
     ll sum = 0;
     cin >> n;
     vector<ll> a(n);
     for (int i = 0; i < n; ++i) cin >> a[i], sum += a[i];
     ll p = sum > 0 ? (sum + n - 1) / n : sum / n, chg = 0;
     for (int i = 0; i < n; ++i) {
         if (a[i] < p) chg += p - a[i];
    }
     cout << chg << endl;
 }

E Not Equal

我们需要求解序列 $a’$ ,使得对于所有 $1\leq i<n$ ,都有 $a’_i\neq a’_{i+1}$ ,修改的代价为:

  • 如果 $a’_i>a_i$ ,由于每次加 $1$ 花费 $b_i$ ,总代价为 $(a’_i−a_i)\times b_i$ 。
  • 如果 $a’_i<a_i$ ,由于每次减 $1$ 花费 $c_i$ ,总代价为 $(a_i−a’_i)\times c_i$ 。
  • 如果 $a’_i=a_i$ ,代价为 $0$ 。

直观来看,修改某个数是为了让它避开与相邻元素的冲突。假设某位置的最优取值为 $a’_i$ ,如果它距离初始值 $a_i$ 非常远(例如 $∣a’_i−a_i∣\geq 3$ ),那么在离 $a_i$ 更近的方向上,比如正数区间 $max⁡(1,a_i−2)$ 到 $a_i+2$ 之间,必然存在某个值既不等于 $a’_{i−1}$ 也不等于 $a’_{i+1}$ ,因为这 $5$ 个数的候选区间里,左右相邻元素最多只会占据 $2$ 个名额,至少剩下 $3$ 个合法的候选值可选。

因此,将 $a’_i$ 调整到离 $a_i$ 更近的合法值,可以严格减小(或维持)修改该元素的代价,同时不破坏相邻不相同的约束。

根据以上结论,我们使用动态规划进行求解,用 $dp[i][j]$ 表示处理前 $i$ 个数,且第 $i$ 个数的最终值为 $v_i$ 中的第 $j$ 个候选值时,所需的最少总花费。有:

$$dp[i][j] = cost(i,v_i[j])+\min\{dp[i-1][k]\ |\ v_{i-1}[k]\neq v_i[j]\}$$

用滚动 $\text{dp}$ 实现。

 void solve() {
     int n;
     cin >> n;
     vector<ll> a(n), b(n), c(n);
     for (int i = 0; i < n; ++i) cin >> a[i];
     for (int i = 0; i < n; ++i) cin >> b[i];
     for (int i = 0; i < n; ++i) cin >> c[i];
 ​
     array<ll, 5> v{}, dp{};
     int siz = 0;
     for (int i = 0; i < n; ++i) {
         array<ll, 5> nv{}, ndp{};
         int nsiz = 0;
         for (ll j = a[i] - 2; j <= a[i] + 2; ++j) {
             if (j >= 1) {
                 nv[nsiz++] = j;
            }
        }
 ​
         for (int j = 0; j < nsiz; ++j) {
             ll V = nv[j], cost = 0;
 ​
             if (V > a[i])
                 cost = (V - a[i]) * b[i];
             else if (V < a[i])
                 cost = (a[i] - V) * c[i];
 ​
             if (i == 0) {
                 ndp[j] = cost;
            } else {
                 ll mn = INF;
                 for (int k = 0; k < siz; k++) {
                     if (v[k] != V) {
                         mn = min(mn, dp[k]);
                    }
                }
                 ndp[j] = mn + cost;
            }
        }
 ​
         for (int j = 0; j < nsiz; ++j) {
             v[j] = nv[j];
             dp[j] = ndp[j];
        }
         siz = nsiz;
    }
 ​
     ll ans = INF;
     for (int j = 0; j < siz; ++j)
         if (dp[j] < ans) ans = dp[j];
     cout << ans << endl;
 }

F Alone

我们先考虑任意一个格子 $(i,j)$ ,如果它是孤立点,那么剩下的 $(n-1)\times(m-1)$ 的格子能够自由染色。这些格子组成了独立于第 $i$ 行和第 $j$ 列的一个大矩形。因此,有 $2^{(n-1)\times(m-1)}$ 种方法来填充这部分 。

然后我们考虑预设点:

对于我们选定的 $(i, j)$ :

  • 如果某个预设点 $(x, y)$ 位于第 $i$ 行或第 $j$ 列,但不是 $(i, j)$ 本身,那么它与 $(i, j)$ 的孤立配置是矛盾的。这种情况下,$(i, j)$ 永远不可能成为孤立点,贡献为 $0$ 。
  • 如果所有 $k$ 个预设点都不与 $(i, j)$ 的孤立配置矛盾,那么这些预设点就必须满足它们各自的条件。
    • 每个位于 $(n-1)\times (m-1)$ 自由区域内的预设点,都必须是黑色。这个要求将该区域的自由度减半。
    • 如果 $(i, j)$​ 本身就是一个预设点,它的黑色要求与孤立配置一致,不产生矛盾。

设有 $xx$ 个不同行, $yy$ 个不同列有预设点。

对于每一个不是预设点的格子,一共有 $(n-xx)\times (m-yy)$ 个格子。对于其中任何一个格子 $(i, j)$ ,它的孤立配置与 $k$ 个预设点都不矛盾。这 $k$ 个点全部落在 $(n-1)\times (m-1)$ 的自由区域内。为了满足这 $k$ 个点的黑色要求,自由区域的填法从 $2^{(n-1)\times(m-1)}$ 种减少到了 $2^{(n-1)\times(m-1) – k}$ 种。

  • 此部分贡献 $= (n-xx) \times (m-yy) \times 2^{(n-1)\times (m-1) – k}$ 。

对于每一个是预设点,且不冲突的格子,记一共有 $K$ 个这样的格子。它的孤立配置与其他的 $k-1$ 个预设点都不矛盾。这 $k-1$ 个点全部落在 $(n-1)\times (m-1)$ 的自由区域内。为了满足这 $k-1$ 个点的黑色要求,自由区域的填法从 $2^{(n-1)\times (m-1)}$ 种减少到了 $2^{(n-1)\times (m-1) – (k-1)}$ 种。

  • 此部分贡献 $= K \times 2^{(n-1)\times (m-1) – k + 1} = K \times 2\times 2^{(n-1)\times (m-1) – k}$

总贡献即为:$((n-xx)\times(m-yy)+K \times 2)\times 2^{(n-1)\times (m-1) – k}$ 。

其中 $(n-1)\times(m-1)$ 可能达到 $10^{18}$ 量级,需要用费马小定理进行优化。

$$a^{p-1}\equiv 1\pmod p$$

所以

$$a^e=a^{k(p-1)+r}=(a^{p-1})^ka^r\equiv a^r\pmod p$$

 void solve() {
     int n, m, k;
     cin >> n >> m >> k;
     vector<PII> a(k);
     unordered_map<int, int> X, Y;
     for (int i = 0; i < k; ++i) cin >> a[i].fi >> a[i].se, X[a[i].fi]++, Y[a[i].se]++;
     int xx = X.size(), yy = Y.size();
     int K = 0;
     for (auto [x, y] : a)
         if (X[x] == 1 && Y[y] == 1) K++;
     ll mod = 998244352;
     cout << (Z(1) * (n - xx) * (m - yy) + Z(2) * K) * ksm(Z(2), ((n - 1) * (m - 1) % mod - k + mod) % mod) << endl;
 }

头文件

 #include <bits/stdc++.h>
 #define pb push_back
 #define pf push_front
 #define pob pop_back
 #define pof pop_front
 #define eb emplace_back
 #define fi first
 #define se second
 #define all(a) a.begin(), a.end()
 #define rall(a) a.rbegin(), a.rend()
 #define endl "\n"
 using namespace std;
 ​
 using ll = long long;
 using ld = long double;
 using ui = unsigned;
 using ull = unsigned long long;
 using i128 = __int128;
 using PII = pair<int, int>;
 using PLL = pair<ll, ll>;
 ​
 void init() {
 }
 ​
 void solve() {
 }
 ​
 int main() {
     ios::sync_with_stdio(0);
     cin.tie(0);
 ​
     init();
 ​
     int t = 1;
     cin >> t;
     cout << fixed << setprecision(15);
     for (int _ = 1; _ <= t; ++_) {
         solve();
    }
 ​
     return 0;
 }

自动取模

constexpr int MOD = 998244353;

template <class T>
constexpr T ksm(T a, ll b) {
T res = 1;
while (b) {
if (b & 1) res *= a;
b >>= 1;
a *= a;
}
return res;
}

template <int P>
struct MInt {
int x;
constexpr MInt() : x() {}
constexpr MInt(ll x) : x{norm(x % getMod())} {}
static int Mod;
constexpr static int getMod() {
if (P > 0) {
return P;
} else {
return Mod;
}
}
constexpr static void setMod(int Mod_) { Mod = Mod_; }
constexpr int norm(int x) const {
if (x < 0) {
x += getMod();
}
if (x >= getMod()) {
x -= getMod();
}
return x;
}
constexpr int val() const { return x; }
explicit constexpr operator int() const { return x; }
constexpr MInt operator-() const {
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv() const {
assert(x != 0);
return ksm(*this, getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs) & {
x = 1LL * x * rhs.x % getMod();
return *this;
}
constexpr MInt &operator+=(MInt rhs) & {
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs) & {
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs) & { return *this *= rhs.inv(); }
friend constexpr MInt operator*(MInt lhs, MInt rhs) {
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs, MInt rhs) {
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs, MInt rhs) {
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs, MInt rhs) {
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr istream &operator>>(istream &is, MInt &a) {
ll v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr ostream &operator<<(ostream &os, const MInt &a) { return os << a.val(); }
friend constexpr bool operator==(MInt lhs, MInt rhs) { return lhs.val() == rhs.val(); }
friend constexpr bool operator!=(MInt lhs, MInt rhs) { return lhs.val() != rhs.val(); }
};

template <>
int MInt<0>::Mod = 998244353;

template <int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();

using Z = MInt<MOD>;
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇