Educational Codeforces Round 170 (Rated for Div. 2)
A. Two Screens
难点是读题,找到最长公共前缀即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
void solve(){
string s, t;
cin >> s >> t;
int res = s.size() + t.size();
int i = 0;
while(i < s.size() and i < t.size() and s[i] == t[i])
i ++, res --;
if(i > 0) res ++;
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --) solve();
return 0;
}
B. Binomial Coefficients, Kind Of
打表找规律
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int N = 1e5;
vi f(N + 1);
f[0] = 1;
for(int i = 1; i <= N; i ++)
f[i] = f[i - 1] * 2 % mod;
int T;
cin >> T;
vi n(T), k(T);
for(auto &i : n) cin >> i;
for(auto &i : k) cin >> i;
for(int i = 0 ; i < T; i ++) {
if(n[i] == k[i]) cout << "1\n";
else cout << f[k[i]] << "\n";
}
return 0;
}
C. New Game
首先我们可以统计出每个数字出现的次数。
可以用双指针求出最大的\([l,r]\),满足区间内的数至少出现一次且\(r < l + k\)。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
void solve(){
int n, k;
cin >> n >> k;
map<int, int> cnt;
for(int i = 0, x; i < n ; i ++)
cin >> x, cnt[x] ++;
vector<pii> a;
for(auto it : cnt) a.push_back(it);
int m = a.size();
int res = 0, sum = 0;
for(int l = 0, r = -1; r < m;) {
if(r < l) {
if(l >= m) break;
assert(sum == 0);
sum = a[l].second , r = l;
}
while(r + 1 < m and a[r + 1].first < a[l].first + k and a[r + 1].first == a[r].first + 1)
r ++, sum += a[r].second;
res = max(res, sum);
sum -= a[l].second, l ++;
}
cout << res << "\n";
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while(T --) solve();
return 0;
}
D. Attribute Checks
考虑前缀中\(i\)中有\(sum\)点属性点,我们用\(f[x]\)表示当前分配\(x\)点智力,\(y = sum - x\)点力量可以满足多少次检查。
现在有三种情况。
如果当前是智力检测\(r_i\),则分配了\(x\in[r_i, sum]\)的\(f[x]\)均加\(1\)。
如果当前是力量检测\(r_i\),则分配了\(y\in[-r_i, sum]\)的\(f[x]\)均加\(1\)。
这两种情况是区间修改。
如果当前是一个待分配的智力点,则\(f[x]\)可以从\(f[x],f[x-1]\)转移过来。
这种情况是单点查询。
因此我们可以用一个树状数组来维护。
但是,我们注意到\(dp\)操作,因此我们可以直接新建一个树状数组更新好之后再赋值回去。
\(f[x]\)的值域是\(m\),至多重建\(m\)次,因此重建的总复杂度是\(O(m^2\log m)\)。
区间修改的次数是\(n\),复杂度是\(O(n\log m)\)。
因此最终的复杂度就是\(O(n \log m)\)。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
struct BinaryIndexedTree{
#define lowbit(x) ( x & -x )
int n;
vector<int> b;
BinaryIndexedTree(int n) : n(n) , b(n + 1 , 0){};
void modify(int i , int y) {
for(; i <= n ; i += lowbit(i) ) b[i] += y;
return;
}
void modify(int l, int r, int y) {
l ++ , r ++;
modify(l, y), modify(r + 1, -y);
}
int calc(int i){
i ++;
int sum = 0;
for( ; i ; i -= lowbit(i) ) sum += b[i];
return sum;
}
};
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
int sum = 0;
BinaryIndexedTree f(m + 1);
for(int i = 0 , x ; i < n; i ++) {
cin >> x;
if(x == 0) {
sum ++;
BinaryIndexedTree g(m + 1);
g.modify(0 , 0, f.calc(0));
for(int j = 1, x; j <= sum; j ++) {
x = max(f.calc(j), f.calc(j - 1));
g.modify(j, j, x);
}
f = move(g);
} else if(x > 0) {
if(x > sum) continue;
f.modify(x, sum , 1);
} else {
x = - x;
if(x > sum) continue;
f.modify(0, sum - x, 1);
}
}
int res = 0;
for(int i = 0; i <= m; i ++)
res = max(res, f.calc(i));
cout << res;
return 0;
}
其实可以注意到区间只有当获得能力值是才会查询,并且每次查询是要查询全部值。因此我们可以直接用差分来实现区间修改,对于查询我们可以直接求出前缀和,然后再进行转移。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
vi f(m + 2);
for (int x, sum = 0; n; n--) {
cin >> x;
if (x == 0) {
sum ++;
for (int i = 1; i <= m; i++)
f[i] += f[i - 1];
for (int i = m; i > 0; i--)
f[i] = max(f[i], f[i - 1]);
for (int i = m; i > 0; i--)
f[i] = f[i] - f[i - 1];
} else if (x > 0) {
if (x > sum) continue;
f[x]++, f[sum + 1]--;
} else {
x = -x;
if (x > sum) continue;
f[0]++, f[sum - x + 1]--;
}
}
for (int i = 1; i <= m; i++)
f[i] += f[i - 1];
cout << ranges::max(f);
return 0;
}
E. Card Game
对于花色为\(1\)的牌,Alice 拿到的牌数一定不少于 Bob。对于花色不为\(1\)的牌,Alice 拿到的牌数一定不多于Bob。
我们在分配的时候可以一次分配两张牌,其中较大的给 Alice,另一张给 Bob。
设 dp 状态\(f[i][j]\)为某种花色的牌,对于前\(i\)张牌,分配了\(i-j\)张牌,剩下\(j\)张牌的方案数,因此一定有\((i-j) | 2\),也就是\(i,j\)奇偶性相同。
对于第\(i\)牌有两种情况。如果不分配,则前\(i-1\)张牌分配了\(j-1\)张。如果分配,则需要一张之前没有分配的牌,因此前\(i-1\)张分配了\(j+1\)张。因此有如下转移
考虑只有第一种花色Alice可以多拿。其他花色只能 Bob 多拿,并且 Bob 每多拿一张,Alice 就要消耗一张花色 1 的牌。因此我们可以记 dp 状态为\(g[i][j]\),表示前\(i\)种花色的牌,此时 Alice还多出来了\(j\)张花色为 1 的牌。对于花色 1 的牌,只能是 Alice 多拿,所以\(g[1][j] = f[m][j]\)。对于其他花色的牌,我们可以枚举出 Bob 多拿了\(k\)张,则存在转移
注意这里的\(j,k\)都只能是偶数。
对于第二个 dp 可以\(O(N^3)\)转移即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int,int>;
const int mod = 998244353;
struct mint {
int x;
mint(int x = 0) : x(x) {}
int val() {
return x = (x % mod + mod) % mod;
}
mint &operator=(int o) { return x = o, *this; }
mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }
mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }
mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; }
mint &operator^=(int b) {
mint w = *this;
mint ret(1);
for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
return x = ret.x, *this;
}
mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }
friend mint operator+(mint a, mint b) { return a += b; }
friend mint operator-(mint a, mint b) { return a -= b; }
friend mint operator*(mint a, mint b) { return a *= b; }
friend mint operator/(mint a, mint b) { return a /= b; }
friend mint operator^(mint a, int b) { return a ^= b; }
};
i32 main() {
int n, m;
cin >> n >> m;
vector f(m + 1, vector<mint>(m + 1));
f[0][0] = 1;
for(int i = 1; i <= m; i ++)
for(int j = 0; j <= i; j ++) {
if(i % 2 != j % 2) continue;
if(j - 1 >= 0) f[i][j] += f[i - 1][j - 1];
if(j + 1 <= m) f[i][j] += f[i - 1][j + 1];
}
vector g(n + 1, vector<mint>(m + 1));
for(int i = 0; i <= m; i += 2)
g[1][i] = f[m][i];
for(int i = 2; i <= n; i ++) {
for(int j = 0; j <= m; j += 2) {
for(int k = 0; k + j <= m; k += 2) {
g[i][j] += g[i - 1][k + j] * f[m][k];
}
}
}
cout << g[n][0].val();
return 0;
}