Educational Codeforces Round 170 (Rated for Div. 2)

1|0A. 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; }

2|0B. 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; }

3|0C. 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; }  

4|0D. Attribute Checks


考虑前缀中i中有sum点属性点,我们用f[x]表示当前分配x点智力,y=sumx点力量可以满足多少次检查。

现在有三种情况。

如果当前是智力检测ri,则分配了x[ri,sum]f[x]均加1

如果当前是力量检测ri,则分配了y[ri,sum]f[x]均加1

这两种情况是区间修改。

如果当前是一个待分配的智力点,则f[x]可以从f[x],f[x1]转移过来。

这种情况是单点查询。

因此我们可以用一个树状数组来维护。

但是,我们注意到dp操作,因此我们可以直接新建一个树状数组更新好之后再赋值回去。

f[x]的值域是m,至多重建m次,因此重建的总复杂度是O(m2logm)

区间修改的次数是n,复杂度是O(nlogm)

因此最终的复杂度就是O(nlogm)

#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; }

5|0E. Card Game


对于花色为1的牌,Alice 拿到的牌数一定不少于 Bob。对于花色不为1的牌,Alice 拿到的牌数一定不多于Bob。

我们在分配的时候可以一次分配两张牌,其中较大的给 Alice,另一张给 Bob。

设 dp 状态f[i][j]为某种花色的牌,对于前i张牌,分配了ij张牌,剩下j张牌的方案数,因此一定有(ij)|2,也就是i,j奇偶性相同。

对于第i牌有两种情况。如果不分配,则前i1张牌分配了j1张。如果分配,则需要一张之前没有分配的牌,因此前i1张分配了j+1张。因此有如下转移

f[i][j]=f[i1][j1]+f[i1][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张,则存在转移

g[i][j]+=g[i1][j+k]×f[m][k]

注意这里的j,k都只能是偶数。

对于第二个 dp 可以O(N3)转移即可。

#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; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18470071.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示