牛客周赛 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
记录一下每个数字值与下标之差,取最大即可。
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
不难发现当 的时候可以选择任意
,进行
的置换,因此必然有解。
剩下的情况特判一下相等即可, 的时候还需要特判一下是否是逆序。
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
由于两个操作对于总值的影响分别为 和
,因此最终的和必然大于等于初始的
,那么最优解显然是取最终值为
,又因为必然大于等于,所以减少的次数一定小于增加的次数,于是我们可以把所有减少的次数都与增加的次数绑组,贪心修改即可。本题需要注意负数的上取整处理。
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
我们需要求解序列 ,使得对于所有
,都有
,修改的代价为:
- 如果
,由于每次加
花费
,总代价为
。
- 如果
,由于每次减
花费
,总代价为
。
- 如果
,代价为
。
直观来看,修改某个数是为了让它避开与相邻元素的冲突。假设某位置的最优取值为 ,如果它距离初始值
非常远(例如
),那么在离
更近的方向上,比如正数区间
到
之间,必然存在某个值既不等于
也不等于
,因为这
个数的候选区间里,左右相邻元素最多只会占据
个名额,至少剩下
个合法的候选值可选。
因此,将 调整到离
更近的合法值,可以严格减小(或维持)修改该元素的代价,同时不破坏相邻不相同的约束。
根据以上结论,我们使用动态规划进行求解,用 表示处理前
个数,且第
个数的最终值为
中的第
个候选值时,所需的最少总花费。有:
用滚动 实现。
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
我们先考虑任意一个格子 ,如果它是孤立点,那么剩下的
的格子能够自由染色。这些格子组成了独立于第
行和第
列的一个大矩形。因此,有
种方法来填充这部分 。
然后我们考虑预设点:
对于我们选定的 :
- 如果某个预设点
位于第
行或第
列,但不是
本身,那么它与
的孤立配置是矛盾的。这种情况下,
永远不可能成为孤立点,贡献为
。
- 如果所有
个预设点都不与
的孤立配置矛盾,那么这些预设点就必须满足它们各自的条件。
- 每个位于
自由区域内的预设点,都必须是黑色。这个要求将该区域的自由度减半。
- 如果
本身就是一个预设点,它的黑色要求与孤立配置一致,不产生矛盾。
- 每个位于
设有 个不同行,
个不同列有预设点。
对于每一个不是预设点的格子,一共有 个格子。对于其中任何一个格子
,它的孤立配置与
个预设点都不矛盾。这
个点全部落在
的自由区域内。为了满足这
个点的黑色要求,自由区域的填法从
种减少到了
种。
- 此部分贡献
。
对于每一个是预设点,且不冲突的格子,记一共有 个这样的格子。它的孤立配置与其他的
个预设点都不矛盾。这
个点全部落在
的自由区域内。为了满足这
个点的黑色要求,自由区域的填法从
种减少到了
种。
- 此部分贡献
总贡献即为: 。
其中 可能达到
量级,需要用费马小定理进行优化。
所以
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>;

京公网安备 11010502036488号