來源:Codeforces Round 970 (Div. 3)
- 標頭檔案
#include <bits/stdc++.h>
using namespace std;
#define YES "YES"
#define NO "NO"
#define Yes "Yes"
#define No "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (int i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>
A. Sakurako's Exam
思路
看1能不能來抵消2,如果沒有1就看2的數量,有1就看是奇數還是偶數,如果是偶數就能抵消2
程式碼
string solve() {
int a, b;
cin >> a >> b;
if (a == 0) {
if (b % 2 == 0)
return YES;
else
return NO;
}
if (a % 2 == 1) {
return NO;
} else {
return YES;
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
cout << solve() << endl;
}
return 0;
}
B. Square or Not
思路
先看長度符不符合要求
再特判一下有沒有0的情況,除n=4外其他沒有0都輸出No
然後根據n求邊長,列舉所有點
程式碼
string s;
string solve() {
int n;
cin >> n >> s;
if ((int)sqrt(n) * (int)sqrt(n) != n) {
return No;
}
if (s.find('0') == string::npos) {
if (n == 4)
return Yes;
else
return No;
}
int a = (int)sqrt(n);
for (int i = 0; i < a; i++) {
for (int j = 0; j < a; j++) {
char ch = s[i * a + j];
char sch;// 應該是1或0
if (i == 0 || j == 0 || i == a - 1 || j == a - 1)
sch = '1';
else
sch = '0';
if (ch != sch) {// 實際與理論比較一下
return No;
}
}
}
return Yes;
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
cout << solve() << endl;
}
return 0;
}
C. Longest Good Array
思路
最優情況就是從 \(l\) 開始,每個差值都差 \(1\)
然後開頭到結尾的就相差 \(1+2+3+...\) 所以用求和公式或者一個陣列就行
程式碼
const int N = 5e4;
int f[N];
int solve() {
int l, r;
cin >> l >> r;
int len = r - l + 1;
int ans = lower_bound(f, f + N, len) - f;
return ans;
}
void pre() {
f[0] = 0;
f[1] = 1;
for (int i = 2; i < N; i++) f[i] = f[i - 1] + i;
}
signed main() {
IOS;
int t = 1;
pre();
cin >> t;
while (t--) {
cout << solve() << endl;
}
return 0;
}
D. Sakurako's Hobby
思路
並查集,因為是全排列,就是好幾個圈圈,一開始還以為會出現環上帶線,那就麻煩了
程式碼
const int N = 2e5 + 10;
string s;
int fa[N];
int ans[N];
// 並查集
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
fa[find(x)] = find(y);
}
void solve() {
int n;
cin >> n;
rep(i, 1, n + 1) {
fa[i] = i;
ans[i] = 0;
}
rep(i, 1, n + 1) {
int tmp;
cin >> tmp;
merge(i, tmp);
}
cin >> s;
rep(i, 0, n) {
// s的0表示黑色
if (s[i] == '0') ans[find(fa[i + 1])]++;
}
rep(i, 1, n + 1) cout << ans[find(fa[i])] << " ";
cout << endl;
}
E. Alternating String
思路
如果是偶數就把奇數上最多的保留,偶數位上最多的字母保留
如果是奇數,就維護字首和,然後遍歷每個位置
刪除當前位置後
如果想算奇數上的 \(a\) 字母,就是當前位置前的奇數位上的 \(a\) 的數量+當前位置後的偶數位上的 \(a\) 的數量
把奇數和偶數上的最大數算出來,再跟目前的最大數比較,和較大的更優
程式碼
int n;
string s;
const int N = 2e5 + 10;
int sum[26][2][N];// sum[i][j][k] 從0到k位,奇(1)/偶(0)位上字母i的數量
int solve() {
cin >> n >> s;
if (n % 2 == 0) {// 不刪,算數量就行
map<int, int> ji_mp;
map<int, int> ou_mp;
for (int i = 0; i < n; i += 2) ou_mp[s[i] - 'a']++;
for (int i = 1; i < n; i += 2) ji_mp[s[i] - 'a']++;
int ji_mx = 0;
int ou_mx = 0;
for (int i = 0; i < 26; i++) {
ji_mx = max(ji_mx, ji_mp[i]);
ou_mx = max(ou_mx, ou_mp[i]);
}
return n - ji_mx - ou_mx;
}
// 初始化
rep(i, 0, 26) {
rep(j, 0, n + 2) {
sum[i][0][j] = 0;
sum[i][1][j] = 0;
}
}
// 算累加
for (int i = 0; i < n; i++) {
if (i % 2 == 0)
sum[s[i] - 'a'][0][i + 1] = 1;
else
sum[s[i] - 'a'][1][i + 1] = 1;
for (int j = 0; j < 26; j++) {
sum[j][0][i + 1] += sum[j][0][i];
sum[j][1][i + 1] += sum[j][1][i];
}
}
int ji_mx = 0;
int ou_mx = 0;
for (int i = 1; i <= n; i++) {
int tjimx = 0, toumx = 0;
// 求最大
for (int j = 0; j < 26; j++) {
int ji_num = sum[j][1][i - 1] + (sum[j][0][n] - sum[j][0][i]);
int ou_num = sum[j][0][i - 1] + (sum[j][1][n] - sum[j][1][i]);
tjimx = max(tjimx, ji_num);
toumx = max(toumx, ou_num);
}
// 比較當前位置的最大和總的最大
if (tjimx + toumx > ji_mx + ou_mx) {
ji_mx = tjimx;
ou_mx = toumx;
}
}
return n - 1 - ji_mx - ou_mx + 1;
}
F. Sakurako's Box
思路
以 \([1,2,3,4,5]\) 為例,暴力會超時,所以維護字尾和,
\(1*(2+3+4+5)\)
\(2*(3+4+5)\)
\(3*(4+5)\)
\(4*5\)
然後總共抽 \(\frac{(n-1)n}{2}\) 次,這題大概主要是考逆元
程式碼
// int用long long來替,避免溢位
const int MOD = 1e9 + 7;
const int N = 2e5 + 10;
int ksm(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int sum[N]; // 字尾和
int a[N];
int solve() {
int n;
cin >> n;
rep(i, 1, n + 1) {
cin >> sum[i];
a[i] = sum[i];
}
for (int i = n - 1; i >= 1; i--) {
sum[i] += sum[i + 1];
sum[i] %= MOD;
}
int s = 0;
rep(i, 1, n) {
int tmp = (a[i] * sum[i + 1]) % MOD;
s = (s + tmp) % MOD;
}
int k = ((n - 1) * n / 2) % MOD;
if (s % k == 0) return s / k;
return (ksm(k, MOD - 2) * s) % MOD;
}
G. Sakurako's Task
思路
加來減去這種題,很可能跟gcd有關係,
首先分析一下,所有數的gcd一定小於等於最小值,比如gcd=2時,n=5,那麼一定可以透過這個2把陣列變成 \([0,2,4,6,8]\) 這種,在這種序列下的答案是最大的
特判一下n=1的情況
程式碼
int n, k;
int a[200005];
int solve() {
cin >> n >> k;
rep(i, 1, n + 1) cin >> a[i];
if (n == 1) {
if (k <= a[1])
return k - 1;
else
return k;
}
// 求所有的gcd
int GCD = 0;
rep(i, 1, n + 1) GCD = __gcd(GCD, a[i]);
// 如果k很大
if ((n - 1) * (GCD - 1) < k) return n - 1 + k;
int re = (k - 1) / (GCD - 1); // region
/*
如gcd=3時,陣列是[0,3,6,9] ,k=1或2,位於0到3之間,k=4或5,位於3到6之間
相當於位於區域0和區域1,每個區域大小是gcd-1
*/
return re * GCD + (k - 1) % (GCD - 1) + 1;
}
H. Sakurako's Test
思路
最小中位數,就是把所有值都儘量縮小,那就所有值取%x
二分答案,答案一定在0到x之間
每次check檢視比mid小的值的數量,如果數量過多說明mid過大,需要減小,反之需要增加
以下是需要注意的:
- 這個二分,有時候是r-1,l+1,有時候又不用,所以我一般是記錄答案,如果mid跟答案一樣就break,基本沒問題,也不用反覆嘗試
- 這個check,如果每次遍歷,經過親身經歷,會超時,所以需要記錄數量,然後把每個%x後比mid小的區間加上,就是需要一個字首和來加速
- 本題會重複詢問同一個值,需要記憶化答案
程式碼
int cnt[200010];
int n;
bool check(int mid, int q) {
int small = 0;
small += cnt[mid - 1];
for (int i = 1; 1; i++) {
if (mid - 1 + q * i >= n) {
if (i * q - 1 <= n) small += cnt[n] - cnt[i * q - 1];
break;
}
small += cnt[mid - 1 + q * i] - cnt[i * q - 1];
}
if (small <= n / 2)
return true;
else
return false;
}
int t;
pii ans[100001];// 記憶化答案
void solve() {
int m;
cin >> n >> m;
rep(i, 0, n + 1) cnt[i] = 0;
rep(i, 0, n) {
int x;
cin >> x;
cnt[x]++;
}
rep(i, 1, n + 1) cnt[i] += cnt[i - 1];
rep(i, 0, m) {
int q;
cin >> q;
if (ans[q].F == t) {
cout << ans[q].S << " ";
continue;
}
int l = 0, r = q;
int mid;
int res = 0;
while (l < r) {
mid = (l + r) / 2;
if (check(mid, q)) {// 不需要思考,莽上去就行
if (res == mid) break;
res = mid;
l = mid;
} else {
r = mid;
}
}
cout << res << " ";
ans[q] = {t, res};
}
cout << endl;
}
signed main() {
IOS;
for (int i = 0; i < 100001; i++) {
ans[i] = {-1, -1};
}
cin >> t;
while (t--) solve();
return 0;
}