來源:Educational Codeforces Round 170 (Rated for Div. 2)
A. Two Screens
題意
給兩個螢幕,兩種操作,每種操作一秒,求讓兩個螢幕顯示兩個指定字串的最短時間
操作:
- 在一個螢幕的字串後加任意一個字元
- 把一個螢幕的內容複製貼上到另一個螢幕
思路
先弄出相同字首,複製一下,然後不相同的只能用操作1來一個一個加
程式碼
string s1, s2;
int main() {
IOS;
int t;
cin >> t;
while (t--) {
cin >> s1 >> s2;
int same = 0;
for (int i = 0; i < min(s1.length(), s2.length()); i++) {
if (s1[i] == s2[i]) same++;
else break;
}
cout << s1.length() + s2.length() - same + (same == 0 ? 0 : 1) << endl;
}
return 0;
}
B. Binomial Coefficients, Kind Of
思路
這邊建議,複製貼上給的程式碼,然後列印一下
程式碼
const int MOD = 1e9 + 7;
long long ksm(long long a, long long b, long long MOD) {
long long res = 1;
while (b) {
if (b & 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
int main() {
IOS;
// int C[20][20];
// for (int n = 0; n < 20; n++) { // loop over n from 0 to N-1 (inclusive)
// C[n][0] = 1;
// C[n][n] = 1;
// for (int k = 1; k < n; k++) // loop over k from 1 to n-1 (inclusive)
// C[n][k] = C[n][k - 1] + C[n - 1][k - 1];
// }
// for (int n = 0; n < 20; n++) { // loop over n from 0 to N-1 (inclusive)
// for (int k = 1; k < n; k++) { cout << C[n][k] << " "; }
// cout << endl;
// }
int t;
cin >> t;
vector<int> n(t), k(t);
for (int &i : n) cin >> i;
for (int &i : k) cin >> i;
for (int i = 0; i < t; i++) {
cout << ksm(2, k[i], MOD) << endl;
}
return 0;
}
C. New Game
題意
一堆數字,第一次選 \(x\),以後只能選 \(x\) 或 \(x+1\)
所選牌中不同的牌不超過 \(k\) 張
思路
首先想用個 \(cnt\) 陣列存每張牌的數量,用雙指標維護一個視窗,視窗內的都是連續的數字
由於每次擴充視窗的時候要判斷下一個位置和當前位置差值是不是1,我選擇把中間連線的 \(cnt\) 搞成一個負數
遇到負數就重新搞視窗
程式碼
const int N = 4e5 + 10;
int cnt[N];
map<int, int> mp;
void solve() {
mp.clear();
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
int tmp;
cin >> tmp;
mp[tmp]++;
}
int turn = 0; // 有多少數和間隔
int pre = 0;
for (auto [num, c] : mp) {
if (num != pre && num != pre + 1) {
cnt[++turn] = -1;
}
pre = num;
cnt[++turn] = c;
}
int l = 1, r = 0;
int now = 0;
int ans = 0;
while (r <= turn && l <= turn) {
while (r - l + 1 < k && now + cnt[r + 1] >= now && r + 1 <= turn) now += cnt[++r];
ans = max(ans, now);
if (now + cnt[r + 1] < now || r + 1 > turn) { // 如果是遇到負數
now = 0;
l = r + 2;
r++;
} else {
now -= cnt[l++];
}
}
cout << ans << endl;
}
D. Attribute Checks
題意
升級打怪,給一個序列,按順序,遇到0就是升級,但是有兩個屬性
遇到正數和負數分別代表遇到針對兩個屬性的怪了
只有自身的對應屬性大於等於怪才能打敗
問最多打得過多少怪(檢查點)
思路
一眼dp,但是n範圍有點不對勁,先不管
- 思路
\(dp[i][j]\) 表示 到目前為止有 \(i\) 分,這 \(i\) 分有 \(j\) 分分配給智力,也就是 \(k=i-j\) 分給體力那麼到第 \(i+1\) 個 \(0\) 之前,最多能過多少檢查點
第 \(i\) 分加在智力上
\(dp[i][j]=dp[i-1][j-1] + ([i-1,i]區間內小於j的正數數量) + (區間內小於k的|負數|數量)\)
第 \(i\) 分加在體力上
\(dp[i][j]=dp[i-1][j] + (區間內小於j的正數數量) + (區間內小於k的|負數|數量)\)
比如按如下順序 \(0(i-1),2, -2, -3, 0(i)\)
當碰到2時,需要對 \(dp[i-1][2\dots(i-1)]\)
每次遇到0的時候都要來一遍狀態轉移,這是不會t的,但是每次碰到檢查點要來一次+1這就是 \(O(mn)\)
就硬交,不死心
- 最佳化
由於有m分,可以先把兩分之間的那些用一個差分陣列存起來,每次遇到0的時候再操作一下,t不了一點
程式碼
空間也可以最佳化,不過這題都可
在原本的陣列後面補一個0,好處理點
const int N = 2e6 + 10;
const int M = 5005;
int a[N];
int dp[M][M];
signed main() {
// IOS;
int n, m;
cin >> n >> m;
rep(i, 0, M) fill_n(dp[i], M, 0);
for (int i = 0; i < n; i++) cin >> a[i];
a[n] = 0;
m++;
n++;
int zero = 0;
vector<int> dif(m + 5, 0);
for (int i = 0; i < n; i++) {
if (a[i] == 0) {
zero++;
for (int j = 1; j <= m + 1; j++) dif[j] += dif[j - 1];
dp[zero][0] = dp[zero - 1][0] + dif[0];
for (int j = 1; j <= zero; j++) {
dp[zero][j] = max(dp[zero - 1][j - 1] + dif[j - 1], dp[zero - 1][j] + dif[j]);
}
for (int j = 0; j <= m; j++) dif[j] = 0;
} else if (a[i] > 0) {
if (a[i] <= zero) {
dif[a[i]]++;
dif[zero + 1]--;
}
} else if (a[i] < 0) {
if (-a[i] <= zero) {
dif[0]++;
dif[zero + a[i] + 1]--;
}
}
}
int ans = 0;
for (int i = 0; i <= zero; i++) {
ans = max(ans, dp[zero][i]);
}
cout << ans;
return 0;
}