A. Wonderful Sticks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include<bits/stdc++.h> using namespace std;
using i64 = long long;
void solve() { int n; string s;
cin >> n >> s;
vector<int> f(n + 1);
f[1] = 1; for (int i = 1; i < n; i++) { if (s[i - 1] == '>') { f[i + 1] = 1; } }
vector<int> pos; for (int i = 1; i <= n; i++) { if (f[i]) pos.push_back(i); } int m = pos.size();
vector<int> a(n + 1); int st = n - m + 1; for (int i = 0; i < m; i++) { a[pos[i]] = st + i; }
vector<int> ans; for (int i = n - m; i >= 1; i--) { ans.push_back(i); }
int c = 0; for (int i = 1; i <= n; i++) { if (!f[i]) { a[i] = ans[c++]; } }
for (int i = 1; i <= n; i++) { cout << a[i] << " \n"[i == n]; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while (t--) { solve(); }
return 0; }
|
B. Wonderful Gloves
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<bits/stdc++.h> using namespace std;
using i64 = long long;
void solve() { int n, k; cin >> n >> k;
vector<i64> l(n + 1), r(n + 1); for (int i = 1; i <= n; i++) { cin >> l[i]; } for (int i = 1; i <= n; i++) { cin >> r[i]; }
i64 res = 0; vector<i64> b(n + 1); for (int i = 1; i <= n; i++) { b[i] = min(l[i], r[i]); res += max(l[i], r[i]); }
sort(b.begin() + 1, b.end(), greater<i64>()); i64 sum = 0; for (int i = 1; i <= k - 1; i++) { sum += b[i]; }
i64 ans = res + sum; cout << ans + 1 << '\n'; }
int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while (t--) { solve(); }
return 0; }
|
C. Wonderful City
观察可以发现,行操作和列操作是分开的,如果某一行有两个数字需要更改,那么必须在对应的两列中选一个去改
初始时
dp0=0,dp1=a1 dp0=0,\quad dp1 = a_1dp0=0,dp1=a1
(第 1 行若雇佣,需花 a1a_1a1;否则 0)。
然后对每个相邻行对 (i,i+1)(i,i+1)(i,i+1):
枚举所有列 j=1…nj=1\ldots nj=1…n,计算原始高度差
d=H[i][j]−H[i+1][j]. d = H[i][j] - H[i+1][j].d=H[i][j]−H[i+1][j].
根据 d∈{−1,0,1}d\in{-1,0,1}d∈{−1,0,1},判定「哪几种 (ri,ri+1)(ri,r{i+1})(ri,ri+1) 会导致新高度相等」:
d=0d=0d=0:如果 ri=ri+1ri = r{i+1}ri=ri+1(要么都是 0,要么都是 1)会使 H′[i][j]=H′[i+1][j]H’[i][j]=H’[i+1][j]H′[i][j]=H′[i+1][j];
d=1d=1d=1:如果 (ri,ri+1)=(0,1)(ri,r{i+1})=(0,1)(ri,ri+1)=(0,1) 会使差变成 1+0−1=01+0-1=01+0−1=0;
d=−1d=-1d=−1:如果 (ri,ri+1)=(1,0)(ri,r{i+1})=(1,0)(ri,ri+1)=(1,0) 会使差变成 −1+1−0=0-1+1-0=0−1+1−0=0。
把出现过的“禁止模式”用一个长 4 的布尔数组 tp[0..3] 标记,再只对允许的转移更新:
cpp
复制编辑
// newdp0 = 不雇第 i+1 行;newdp1 = 雇第 i+1 行 newdp0 = min( dp0 if (r_i=0→r_{i+1}=0) 未被 d=0 禁止, dp1 if (r_i=1→r_{i+1}=0) 未被 d=-1 禁止 ); newdp1 = min( dp0 + a[i+1] if (0→1) 未被 d=1 禁止, dp1 + a[i+1] if (1→1) 未被 d=0 禁止 );
如果同时出现所有四种模式,说明这条相邻行边无论如何都不可能保持不等高,直接置 dp0=dp1=inf,提前剪掉。
走完 i=1i=1i=1 到 n − 1n!-!1n−1,最终行方向最小费用取 min(dp0,dp1)\min(dp0,dp1)min(dp0,dp1),记作 XX。
对于列同理,记作YY,取和就是答案;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| #include<bits/stdc++.h> using namespace std;
using i64 = long long; constexpr i64 inf = 1e18 + 7; void solve() { int n; cin >> n;
vector h(n + 1, vector<i64>(n + 1)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cin >> h[i][j]; } }
vector<i64> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1; i <= n; i++) { cin >> b[i]; }
i64 dp0 = 0, dp1 = a[1]; for (int i = 1; i < n; i++) { vector<int> tp(4); for (int j = 1; j <= n; j++) { i64 d = h[i][j] - h[i + 1][j]; if (d == 0) { tp[0] = tp[3] = 1; } else if (d == 1) { tp[1] = 1; } else if (d == -1) { tp[2] = 1; } }
if (tp[0] && tp[1] && tp[2] && tp[3]) { dp0 = dp1 = inf; break; } i64 ndp0 = inf, ndp1 = inf; if (!tp[0]) ndp0 = min(ndp0, dp0); if (!tp[2]) ndp0 = min(ndp0, dp1); if (!tp[1]) ndp1 = min(ndp1, dp0 + a[i + 1]); if (!tp[3]) ndp1 = min(ndp1, dp1 + a[i + 1]);
dp0 = ndp0; dp1 = ndp1; } i64 xx = min(dp0, dp1);
dp0 = 0, dp1 = b[1]; for (int j = 1; j < n; j++) { vector<int> tp(4); for (int i = 1; i <= n; i++) { i64 d = h[i][j] - h[i][j + 1]; if (d == 0) { tp[0] = tp[3] = 1; } else if (d == 1) { tp[1] = 1; } else if (d == -1) { tp[2] = 1; } }
if (tp[0] && tp[1] && tp[2] && tp[3]) { dp0 = dp1 = inf; break; } i64 ndp0 = inf, ndp1 = inf; if (!tp[0]) ndp0 = min(ndp0, dp0); if (!tp[2]) ndp0 = min(ndp0, dp1); if (!tp[1]) ndp1 = min(ndp1, dp0 + b[j + 1]); if (!tp[3]) ndp1 = min(ndp1, dp1 + b[j + 1]);
dp0 = ndp0; dp1 = ndp1; } i64 yy = min(dp0, dp1);
if (xx >= inf / 2 || yy >= inf / 2) { cout << -1 << '\n'; } else { cout << xx + yy << '\n'; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while (t--) { solve(); }
return 0; }
|
D. Wonderful Lightbulbs
疯狂观察, 疯狂在纸上乱花后发现:由于一开始只有一个点,你再怎么操作(不管操作多少次),一开始所在的点的那一条平行于Y轴的直线上都只能有奇数个点,而除了这条直线,其他平行于Y轴的直线都必然是偶数个点。
至此我们确定了X坐标
怎么确定Y坐标呢?
再次观察(神学),发现斜方向(与直线 y = -x平行方向),除了一开始所在点,其他直线都是偶数个,而一开始所在点是奇数个点,
这样我们确定了两个直线,取交点即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| #include<bits/stdc++.h> using namespace std;
using i64 = long long; constexpr i64 inf = 1e18 + 7; void solve() { int n; cin >> n;
vector h(n + 1, vector<i64>(n + 1)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cin >> h[i][j]; } }
vector<i64> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } for (int i = 1; i <= n; i++) { cin >> b[i]; }
i64 dp0 = 0, dp1 = a[1]; for (int i = 1; i < n; i++) { vector<int> tp(4); for (int j = 1; j <= n; j++) { i64 d = h[i][j] - h[i + 1][j]; if (d == 0) { tp[0] = tp[3] = 1; } else if (d == 1) { tp[1] = 1; } else if (d == -1) { tp[2] = 1; } }
if (tp[0] && tp[1] && tp[2] && tp[3]) { dp0 = dp1 = inf; break; } i64 ndp0 = inf, ndp1 = inf; if (!tp[0]) ndp0 = min(ndp0, dp0); if (!tp[2]) ndp0 = min(ndp0, dp1); if (!tp[1]) ndp1 = min(ndp1, dp0 + a[i + 1]); if (!tp[3]) ndp1 = min(ndp1, dp1 + a[i + 1]);
dp0 = ndp0; dp1 = ndp1; } i64 xx = min(dp0, dp1);
dp0 = 0, dp1 = b[1]; for (int j = 1; j < n; j++) { vector<int> tp(4); for (int i = 1; i <= n; i++) { i64 d = h[i][j] - h[i][j + 1]; if (d == 0) { tp[0] = tp[3] = 1; } else if (d == 1) { tp[1] = 1; } else if (d == -1) { tp[2] = 1; } }
if (tp[0] && tp[1] && tp[2] && tp[3]) { dp0 = dp1 = inf; break; } i64 ndp0 = inf, ndp1 = inf; if (!tp[0]) ndp0 = min(ndp0, dp0); if (!tp[2]) ndp0 = min(ndp0, dp1); if (!tp[1]) ndp1 = min(ndp1, dp0 + b[j + 1]); if (!tp[3]) ndp1 = min(ndp1, dp1 + b[j + 1]);
dp0 = ndp0; dp1 = ndp1; } i64 yy = min(dp0, dp1);
if (xx >= inf / 2 || yy >= inf / 2) { cout << -1 << '\n'; } else { cout << xx + yy << '\n'; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while (t--) { solve(); }
return 0; }
|
E. Wonderful Teddy Bears
段落分解
把原串看成一堆 “B…B” 之间夹着的若干 P 段:
先把所有 B 的下标收集到 pos(在最前面加一个 −1 做哨兵),
然后对相邻的两次 B,下标差减 1 就是中间连续的 P 的数目,存入数组 x。
最后再在 x 末尾添加一个长度为 1 的哨兵段(方便后面操作统一处理)。
操作等价
每次在位置 i 的窗口 [i,i+1,i+2] 里把黑熊都挪到左边,相当于:
批量与配对
收尾处理
当再也找不到能配对的 P,就剩下一些 孤立的 P,总数记作 rem。
剩余的消除操作数,可证明是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| #include<bits/stdc++.h> using namespace std;
using i64 = long long;
void solve() { int n; cin >> n;
string s; cin >> s;
vector<int> a(n); for (int i = 0; i < n; i++) { a[i] = (s[i] == 'P'); }
vector<int> pos; pos.push_back(-1);
for (int i = 0; i < n; i++) { if (!a[i]) { pos.push_back(i); } }
int cnt = pos.size();
vector<i64> x; for (int i = 0; i < cnt - 1; i++) { x.push_back(pos[i + 1] - pos[i] - 1); }
x.push_back(1); int M = x.size();
i64 ans = 0; priority_queue<int> pq; for (int i = 0; i < M - 1; i++) { pq.push(i); }
set<int> fst; for (int i = 0; i < M; i++) { fst.insert(i); }
while (!pq.empty()) { int i = pq.top(); pq.pop();
if (i < 0 || i + 1 > M) { continue; }
if (x[i] >= 2) { i64 cur = x[i] / 2; ans += 1LL * cur * (M - 1 - i); x[i] %= 2;
for (int d = -2; d <= 2; d++) { int p = i + d; if (p >= 0 && p + 1 < M) { pq.push(p); } }
continue; }
if (x[i] == 0) { continue; }
int p = i + 1; while (true) { if (p >= M) { break; }
if (x[p] == 0) { fst.erase(p); auto it = fst.upper_bound(p); if (it == fst.end()) break; p = *it; } else { break; } }
if (p >= M) continue;
int k = p; if ((i + k) & 1) k--; if (i == k) continue;
x[i]--; x[k]++; fst.insert(k); ans += (k - i) / 2;
for (int d = -2; d <= 2; d++) { int u = i + d; if (u >= 0 && u + 1 < M) { pq.push(u); }
int v = k + d; if (v >= 0 && v + 1 < M) { pq.push(v); } } }
i64 rem{}; for (int i = 0; i + 1 < M; i++) { rem += x[i]; }
if (rem & 1) { i64 t = rem / 2 + 1; ans += t * t; } else { ans += (rem / 2) * (rem / 2 + 1); }
cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while (t--) { solve(); }
return 0; }
|