I - 完美迴文
題意
把單詞改成一串相同的字母,最小修改次數
思路
把所有字母改成這個單詞中出現次數最多的字母
程式碼
#include <bits/stdc++.h>
using namespace std;
void solve() {
string s;
map<char, int> mp;
cin >> s;
int mx = 0;
for (char ch : s) {
mp[ch]++;
mx = max(mx, mp[ch]);
}
cout << s.size() - mx << '\n';
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
G - 邪惡銘刻
題意
按順序有三種情況
1獲取野獸,攻擊力為1
-1合成兩隻野獸
0選擇1或-1
求最後野獸的平均戰力最高是多少
思路
貪心的考慮0應該怎麼分配
由於算的是平均值,合成野獸的貢獻比增加野獸的貢獻大,那麼儘量選-1,只剩一隻野獸的時候再選1
但是如果出現\(1,1,1,0,-1,-1,\) 這種序列,遇到0取-1是無法完成所有事件的
那麼可以把0位置記錄下來,一旦碰到無法完成所有任務的情況,就把0從-1改成1
程式碼
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int &i : a) cin >> i;
int cnt = 0;
int p = 1, q = 1;
for (int i = 0; i < n; i++) {
if (a[i] == 1) {
p++;
q++;
} else if (a[i] == -1) {
if (q > 1) {
q--;
} else {
if (cnt) {
p++, q++;
cnt--;
} else {
cout << -1 << endl;
return;
}
}
} else {
if (q > 1) {
q--;
cnt++;
} else {
p++, q++;
}
}
}
int z = __gcd(p, q);
cout << p / z << " " << q / z << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
D - 聊天程式
題意
給初始序列 \(a\),要求最大化 \(a\) 的第 \(k\) 大的數
可以對 \(a\) 進行如下操作,給定等差數列的長度\(m\),首項\(c\),公差\(d\)
選擇 \(a\) 的連續序列,把等差數列加上去
最多加1次
思路
採用二分第k大的值
關鍵在於check函式怎麼寫
暴力就是列舉每個位置的 \(a_i\) 作為開頭,加一遍後看看能不能滿足第 \(k\) 大的數大於 \(x\)
即便是暴力方式,也不能每次新建一個陣列來排序,我們選擇用計數的方式
大於等於 \(x\) 的個數大於等於 \(k\) 說明滿足要求(注意這裡的第k大是指從大到小排序的第k個數字,不用去重
那麼接下來思考最佳化,對於等差數列,我們先只觀察一個位置 以\([1,1,4,5,1,4]\)為例
\(c=1,d=2,m=3\),那麼數列就是\(1,3,5\)
假定二分的第k大的值是4,觀察 \(a_4=1\) ,如果 \(a_4\) (下標從0開始)作為等差數列的第二項,是滿足要求的
但是如果作為等差數列的第3項,加起來也比4大
那麼列舉 \(a_2\) 為開頭,從 \(a_2\) 到 \(a_4\) ,排除原本就大於4的值,現在對答案的貢獻就是 \(a_4\) 位置的1個
列舉 \(a_3\) 為開頭,從 \(a_3\) 到 \(a_5\),對答案的貢獻還是 \(a_4\)
每個位置作為等差開頭從而新增的大於 \(x\) 的數量加上原本就大於 \(x\) 的數量如果大於等於 \(k\),return true
差分維護
程式碼
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e5 + 10;
int a[N];
int n, k, m, c, d;
int sum[N];
bool check(int x) {
fill_n(sum, n + 1, 0);
int cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] < x) {
int rk;
if (d != 0) {
if (x <= a[i] + c)
rk = 1;
else
rk = (x - a[i] - c + d - 1) / d + 1;
if (rk > m) continue;
if (i - rk + 1 >= 0) sum[i - rk + 1]++;
if (i - m >= 0) sum[i - m] -= 1;
} else {
if (x - a[i] - c <= 0) {
rk = 0;
sum[i]++;
if (i - m >= 0) {
sum[i - m] -= 1;
}
}
}
} else {
cnt++;
}
}
for (int i = n - 1; i >= 0; i--) {
sum[i] += sum[i + 1];
if (cnt + sum[i] >= k) return 1;
}
return 0;
}
void solve() {
cin >> n >> k >> m >> c >> d;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int l = 0, r = 1e18;
int ans = 0;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
l = mid + 1;
ans = mid;
} else
r = mid - 1;
}
cout << ans;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
int t = 1;
while (t--) {
solve();
}
return 0;
}
A - 停停,昨日請不要再重現
題意
一個棋盤上站滿了袋鼠,給操作序列,袋鼠跟隨操作序列移動,如果跳出棋盤就移除
棋盤上有個洞,掉進洞的袋鼠也被移除
問最後剩下 \(k\) 只袋鼠的話,有多少可能的位置上有洞,每次棋盤上只有一個洞
思路
在沒有洞的情況下,可以判斷出最後剩下的袋鼠是一個固定的矩形部分\(n',m'\)
在有洞的情況下,可以看成 \(n'm'\) 固定,這樣洞就有一個路徑
並且洞的路徑不會超,洞的路徑與操作相反,L說明洞往右走
而如果洞的路徑超了,說明袋鼠全都走出去了,判掉就行
也就是說,我們把洞的路徑看成一個方形g,用[[二維差分]]維護一下
\(g[x][y]\) 表示在 \((x,y),(x-U,y-L)\) 這個矩形內的洞經過的格子數
用所有剩下的袋鼠 \(x-g[x][y]\) 就是剩餘的袋鼠數
程式碼
程式碼抄襲參考這篇
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 1000 + 10;
int n, m, k;
int U, D, L, R, U_, D_, L_, R_;
bool st[N][N]; // stone,表示石頭是否存在在這一格
int g[N][N];
string op;
void add(int x1, int y1, int x2, int y2) {
// 去重,洞走過多次等同於走一次
if (st[x1][y1]) return;
st[x1][y1] = true;
// 差分
g[x1][y1]++;
g[x2 + 1][y1]--;
g[x1][y2 + 1]--;
g[x2 + 1][y2 + 1]++;
}
void solve() {
cin >> n >> m >> k >> op;
// 左上角為座標原點
U_ = L_ = U = L = 1;
R_ = R = m;
D_ = D = n;
memset(st, 0, sizeof st);
memset(g, 0, sizeof g);
// 確定邊界
for (char ch : op) {
// 往左,說明左邊兩行不用了
if (ch == 'L') L_++, R_++;
if (ch == 'R') L_--, R_--;
if (ch == 'U') U_++, D_++;
if (ch == 'D') U_--, D_--;
L = max(L, L_);
R = min(R, R_);
U = max(U, U_);
D = min(D, D_);
}
// 無袋鼠剩餘
if (U > D || L > R) {
// 如果k>0 不可能完成,反之,洞隨便放
if (k)
cout << "0\n";
else
cout << n * m << endl;
return;
}
// 統計袋鼠經過格子的情況
int x = (D - U + 1) * (R - L + 1), cnt = 0;
add(U, L, D, R);
for (char ch : op) {
// 往左,說明洞往右
if (ch == 'L') L--, R--;
if (ch == 'R') L++, R++;
if (ch == 'U') U--, D--;
if (ch == 'D') U++, D++;
add(U, L, D, R);
}
// 二分字首和
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];
}
}
// 統計答案
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (x - g[i][j] == k) cnt++;
}
}
cout << cnt << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
}
M - 清空水箱
題意
逆時針給一個簡單圖形,沒有交叉和重合,但是有共線的,這個圖形是個水箱,裡面有水,要開若干個洞才能把水全排出去
疑惑
題解還是挺多的,但是我有個wa30的思路表示十分疑惑,希望有佬能幫我看看哪裡有問題
如圖,逆時針的話,前一條邊的極角是 \(a1\) ,後一條邊是 \(a2\),用 \(atan2\) 函式來算
只要滿足 \(a1>0\),\(a2>0\) ,\(a1>a2\)就說明需要開口
程式碼
#include <bits/stdc++.h>
using namespace std;
#define ld long double
#define endl '\n'
const double eps = 1e-7;
struct P {
int x, y;
} a[2010];
void solve() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i].x >> a[i].y;
}
a[n].x = a[0].x;
a[n].y = a[0].y;
a[n + 1].x = a[1].x;
a[n + 1].y = a[1].y;
n++;
int ans = 0;
for (int i = 1; i < n; i++) {
long double a1 = atan2l(a[i - 1].y - a[i].y, a[i - 1].x - a[i].x);
long double a2 = atan2l(a[i + 1].y - a[i].y, a[i + 1].x - a[i].x);
if (a1 > eps && a2 > eps) {
if (a2 <= a1) ans++;
}
}
cout << ans << endl;
}
int t = 1;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
while (t--) {
solve();
}
return 0;
}