A. Spread
不說了,程式碼。
B. Next
不說了,程式碼。
C. Count xxx
Description
給定一個長度為 \(N\) 的字串 \(S\),求 \(S\) 中非空連續,並且包含重複字元的連續子串長度。
例如 $S = $ aaabaa
,則它滿足上述條件子串為 a
,aa
,aaa
,b
。
Solution
這道題 \(1 \le N \le 2 \times 10 ^ 5\),顯然不能暴力。
考慮如何最佳化。
維護陣列 \(v\),\(v_i\) 表示 \(S\) 中子串的重複字元為 \(i\) 的子串長度。
我們迴圈這個字串,記錄兩個變數 lstc
,lstnum
,分別表示字串 \(S\) 的上一個字元,和當前連續子串的長度。
如果我們發現,s[i] == lstc
,則 lstnum++
。否則,lstnum = 1
,lstc = s[i]
。
每次迴圈 v[lstc] = max(v[lstc],lstnum)
。
最後迴圈字元 a ~ z
,累加 \(v_i\) 即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, ans, v[300];
char s[N];
int main() {
scanf("%d", &n);
scanf("%s", s + 1);
char lstc = 0;
int lstnum = 0;
for (int i = 1; i <= n; ++i) {
if (s[i] == lstc) {
++lstnum;
} else {
lstnum = 1;
lstc = s[i];
}
v[lstc] = max(v[lstc], lstnum);
}
int ans = 0;
for (int i = 'a'; i <= 'z'; ++i) {
ans += v[i];
}
printf("%d", ans);
return 0;
}
D. Election Quick Report
Description
有 \(N\) 個人,編號 \(1 \sim N\)。每次會將票投給某一個人,輸出投票後票數最大值的人的編號。如果票數相同,輸出編號小的。
Solution
每次記錄一個桶,動態維護當前票數的最大值,位置即可。
如果當前的票數大於最大值,或者當前票數等於最大值但是編號小於最大值,則講最大值更改為當前的票數,並且將位置更改為當前的位置。
每次詢問輸出位置即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, a[N], cnt[N], ma = 0, p;
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
scanf("%d", &a[i]);
cnt[a[i]]++;
if (cnt[a[i]] > ma || (cnt[a[i]] == ma && a[i] < p)) {
ma = cnt[a[i]];
p = a[i];
}
printf("%d\n", p);
}
return 0;
}
E. Stamp
Description
有兩個字串 \(S\) 和 \(T\),還有一個長度為 \(N\) 的僅由字元 #
構成的字串 \(X\)。
現在可以執行若干次操作,每次可以選擇字串 \(X\) 中連續的 \(M\) 段覆蓋為 \(T\)。
求:是否能透過若干次操作,使得 \(X\) 和 \(S\) 相等。
Solution
考慮一個覆蓋時間的序列(欽定每一次覆蓋記錄在其右端點上),發現對於當前點 \(p\):
只有 \([p,p+m-1]\) 這一段區間會影響該點的取值,考慮爆搜,對於當前位置p,只需要考慮 \([p-m+1,p]\) 這一段長度為m的時間序列,列舉當前一個新的位置的相對時間排名,確定下來 \([p-m+1,p]\) 的相對時間順序,於是位置 \(p-m+1\) 的值會被 \([p-m+1,p]\) 中時間值最大的點覆蓋掉,再判斷 \(p-m+1\) 的值是否與 \(S\) 串對應位置相等即可。
考慮最佳化:當前狀態包含當前位置 \(p\),和前面的時間序列 \(st\),若之前已經走到過 <p,st>
這樣一個狀態,那當前就沒必要繼續往下搜了。
Code
#include <bits/stdc++.h>
using namespace std;
unordered_map<int, int> mp;
int n, m;
int gethash(int now, int stat[5]) {
int hsh = now;
for (int i = 0; i < m - 1; i++) hsh = hsh * m + stat[i];
return hsh;
}
char s[200055];
char t[55];
bool search(int now, int stat[5]) {
int hsh = gethash(now, stat);
if (mp.find(hsh) != mp.end()) return false;
mp[hsh] = 1;
if (now == n + 1) {
char x[6];
for (int j = 1; j < m; j++) {
for (int k = 0; k < m - 1; k++) {
if (stat[k] == j) {
for (int l = 2; l <= m - k; l++) {
x[l] = t[l + k];
}
}
}
}
for (int i = 2; i <= m; i++) if (s[n + i - m] != x[i]) return false;
return true;
}
for (int i = 1; i <= m; i++) {
int new_stat[5];
new_stat[0] = i;
for (int j = 1; j < m; j++) {
new_stat[j] = stat[j - 1];
if (stat[j - 1] >= i) ++new_stat[j];
}
bool flag = true;
for (int j = 0; j < m; j++) {
if (new_stat[j] == m) {
if (s[now - m + 1] != t[j + 1]) flag = false;
}
}
if (!flag) continue;
for (int j = 0; j < m - 1; j++) {
if (new_stat[j] > new_stat[m - 1]) --new_stat[j];
}
if (search(now + 1, new_stat)) return true;
}
return false;
}
int stat[5];
int main() {
cin >> n >> m;
scanf("%s%s", s + 1, t + 1);
if (s[1] != t[1]) {
puts("No");
return 0;
}
for (int i = 0; i < 5; i++) stat[i] = m - i - 1;
if (search(m + 1, stat)) {
printf("Yes");
return 0;
}
printf("No");
return 0;
}
F. Colored Ball
Description
有 \(N\) 個箱子,編號為 \(1 \sim N\)。每個箱子初始有一個小球,顏色為 \(C_i\)。
現在有 \(Q\) 次詢問,每次輸入一個二元組 \((a,b)\),表示將編號為 \(a\) 的箱子裡的所有小球移動到箱子 \(b\) 後,詢問箱子 \(b\) 中有多少種不同顏色的小球。
Solution
這道題用 set
的暴力模擬法,時間複雜度約為 \(O(n ^ 2 \times \log n)\),肯定超時。
可以加上最佳化:set
的啟發式合併。
即每次將小的箱子移動到大的箱子裡面,這樣花費的時間會大大減少,時間複雜度達到了 \(O(n)\)。
只需要在程式碼中加入一句話:
if (st[x].size() > st[y].size()) swap(st[x], st[y]);
這樣就實現了啟發式合併,可以透過。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, q, a[N], x, y;
unordered_set<int> st[N];
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
st[i].insert(a[i]);
}
while (q--) {
scanf("%d%d", &x, &y);
if (st[x].size() > st[y].size()) swap(st[x], st[y]);
while (st[x].size()) {
int t = *st[x].begin();
st[x].erase(t);
st[y].insert(t);
}
printf("%d\n", (int)st[y].size());
}
return 0;
}