(马后炮)解题报告.
Table of Contents
- Overview
- A. Benches
- Description
- Solution
- B. Vitamins
- Description
- Solution
- C. Array Product
- Description
- Solution
- D. Petya and Array
- Description
- Solution
- E. Vasya and Magic Matrix
- Description
- Solution
- F. Leaf Sets
- Description
- Solution
Overview
本来是register了, 但是因为上课所以并没有打. 比赛的时候口胡了两个题, 下场调了一会倒是过了, AB比较水就另说. 然后又口胡了一下E, 大概是个期望维护个前缀和就可以转移了的说. 大概也就能做到这样子. F还是有点仙的, 不过不难理解.
A. Benches
Description
长凳上已经坐了一些人, 又要来一些人, 问所有长凳上最多的人的最大可能和最小可能.
Solution
堆+贪心乱搞一通.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int n, a[maxn], m; priority_queue<int, vector<int>, greater<int> >q;
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> a[i], q.push(a[i]); for(int i = 1; i <= m; ++i) { int u = q.top(); q.pop(); u++; q.push(u); } int mx = 0; while(!q.empty()) mx = max(mx, q.top()), q.pop(); cout << mx << " "; sort(a + 1, a + 1 + n); cout << a[n] + m << endl; return 0; }
|
B. Vitamins
Description
每种商品含若干种维他命, 问获取所有维他命的最小花费, 种类 $\le 3$.
Solution
要是种类很多可以DP吧? 不过3嘛, 嘿嘿嘿.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n, co[maxn]; int a, b, c, ab, bc, ac, abc;
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); string s;
a = b = c = 400005; ac = bc = ab = abc = 400005; cin >> n; for(int i = 1; i <= n; ++i) { cin >> co[i] >> s; if(s.size() == 1) { if(s[0] == 'A') a = min(a, co[i]); else if(s[0] == 'B') b = min(b, co[i]); else c = min(c, co[i]); } else if(s.size() == 2) { int has[3] = {0,0,0}; has[s[0]-'A'] = 1; has[s[1]-'A'] = 1; if(has[0] && has[1]) ab = min(ab, co[i]); else if(has[1] && has[2]) bc = min(bc, co[i]); else ac = min(ac, co[i]); } else { abc = min(abc, co[i]); } } int ans = 400005; ans = min(ans, min(a + b + c, abc)); ans = min(ans, min(ab + bc, ab + ac)); ans = min(ans, ac + bc); ans = min(ans, min(ab + c, bc + a)); ans = min(ans, ac + b); if(ans < 400005) cout << ans << endl; else puts("-1"); return 0; }
|
C. Array Product
Description
有两种操作, 第一种是把两个数乘起来放到一个位置, 另一个删掉, 第二种是删掉一个数. 删除后下标不变, 但不能使用. 最大化剩下的一个数的结果.
Solution
有正有负, 肯定就看负数能不能乘出一个整数嘛, 不能就和0合并一下然后删掉.
能的话, 就把0合并一下, 然后删掉.
剩下的无脑乘到第一个没有被删除的位置上就好了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <bits/stdc++.h>
using namespace std;
const int maxn = 2e+5 + 5;
vector<int> zrs; int n, a[maxn], negabs = 0x3f3f3f3f, negpos, negcnt; int vis[maxn];
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n; for(int i = 1; i <= n; ++i) { cin >> a[i]; if(a[i] < 0) { if(abs(a[i]) < negabs) negabs = -a[i], negpos = i; negcnt ^= 1; } if(a[i] == 0) zrs.push_back(i); } if(!negcnt) { if(zrs.size()) { for(int i = 1; i < zrs.size(); ++i) printf("1 %d %d\n", zrs[i], zrs[0]), vis[zrs[i]] = 1; if((int)zrs.size() == n) return 0; printf("2 %d\n", zrs[0]); vis[zrs[0]] = 1; } for(int i = 1; i <= n; ++i) { if(!vis[i]) { for(int j = i + 1; j <= n; ++j) if(!vis[j]) printf("1 %d %d\n", j, i); return 0; } } } else { if(zrs.size()) for(int i = 0; i < zrs.size(); ++i) printf("1 %d %d\n", zrs[i], negpos), vis[zrs[i]] = 1; if((int)zrs.size() == n - 1) return 0; printf("2 %d\n", negpos); vis[negpos] = 1; for(int i = 1; i <= n; ++i) { if(!vis[i]) { for(int j = i + 1; j <= n; ++j) if(!vis[j]) printf("1 %d %d\n", j, i); return 0; } } } return 0; }
|
D. Petya and Array
Description
给一个数组, 求所有和小于 $t$ 的子区间个数.
Solution
如果都是非负数的话, 双指针就好了, 但是有负数.
一个经典思路然而我并没想到, 好像还讲过, 分治, 每次只考虑跨越当前区间中点的答案, 这样在左边维护后缀和, 右边维护前缀和, 排序以后, 是可以双指针的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <bits/stdc++.h>
using namespace std;
const int maxn = 2e+5 + 5;
typedef long long ll;
int a[maxn], n; ll pre[maxn], suf[maxn]; ll ans, t;
void DaC(int l, int r) { if(l == r) { if(a[l] < t) ans++; return; } int mid = (l + r) >> 1; vector<ll> suf, pre; ll sum = 0; for(int i = mid; i >= l; --i) sum += a[i], suf.push_back(sum); sum = 0; for(int i = mid + 1; i <= r; ++i) sum += a[i], pre.push_back(sum); int llen = suf.size(), rlen = pre.size(); sort(suf.begin(), suf.end()); sort(pre.begin(), pre.end()); int j = rlen; for(int i = 1; i <= llen; ++i) { while(j && suf[i - 1] + pre[j - 1] >= t) --j; ans += j; } DaC(l, mid); DaC(mid + 1, r); }
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> t; for(int i = 1; i <= n; ++i) cin >> a[i];
DaC(1, n); cout << ans << endl; return 0; }
|
E. Vasya and Magic Matrix
Description
给一个矩阵, 每个点有值, 每次操作只能从当前的跳到更小值的格子去, 得分是欧几里得距离的平方. 求得分期望.
Solution
先列式子嘛.
然后是转移.
先把式子打开.
这样增量的时候, 把该减的减掉该加的加上就可以增量了, 维护一些前缀和, 前缀平方和, 前缀期望和之类的.
记得相等的值要同时做.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1005; const ll p = 998244353;
struct node { ll x, y; ll v; bool operator < (const node &rhs) const { return v < rhs.v; } }; int n, m; ll sumx, sumy, sumxs, sumys, sumf; ll r, c, f[maxn][maxn], mtr[maxn][maxn]; vector<node> vec;
inline ll quick_power(ll base, ll index) { ll ret = 1; while(index) { if(index & 1) ret = (ret * base) % p; index >>= 1; base = base * base % p; } return ret; } inline void add(ll &res, ll num) { res += num; while(res >= p) res -= p; while(res < 0) res += p; } inline ll prod(ll a, ll b) { return a * b % p; }
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m; for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) { cin >> mtr[i][j]; } cin >> r >> c; r--; c--;
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) vec.push_back((node){i, j, mtr[i][j]});
sort(vec.begin(), vec.end());
int i = 0; while(i < (int)vec.size()) { int j = i; while(j < (int)vec.size() && vec[j].v == vec[i].v) ++j;
ll inv = -1; if(i) inv = quick_power(i, p - 2); for(int k = i; k < j; ++k) { ll xx = vec[k].x, yy = vec[k].y; if(inv == -1) { f[xx][yy] = 0; continue; } add(f[xx][yy], prod(sumf, inv)); add(f[xx][yy], prod(xx, xx)); add(f[xx][yy], prod(yy, yy)); add(f[xx][yy], prod(sumxs, inv)); add(f[xx][yy], prod(sumys, inv)); add(f[xx][yy], prod(prod(-xx * 2ll, sumx), inv)); add(f[xx][yy], prod(prod(-yy * 2ll, sumy), inv));
} for(int k = i; k < j; ++k) { ll xx = vec[k].x, yy = vec[k].y; add(sumf, f[xx][yy]); add(sumxs, prod(xx, xx)); add(sumys, prod(yy, yy)); add(sumx, xx); add(sumy, yy); } i = j; }
cout << f[r][c] << endl; return 0; }
|
F. Leaf Sets
Description
把一颗树的叶子节点划分成若干集合, 使得每个集合内最远点对的距离小于等于 $k$. 求最小集合数.
Solution
本来想二分答案, 比如按深度排个序然后顺次验证什么的, 但是不太可做.
有一个性质, 就是在当前节点 $v$, 记她到某个孩子的子树内最深节点的距离为 $d$.
这样就有了若干个 $d$, 当然是要合并了, 这样才能减少集合, 考虑合并的条件, 当且仅当$d_a + d_b \le k$, 合并后的集合, 应该有: $d_u = max(d_a, d_b)$.
再考虑不能合并, 如果有 $d_a + d_b > k$, 且$d_a \le d_b$. 那么$b$接下来一定没有用了, 因为能和她合并的就能和 $a$ 合并. 而她又不能再合并, 所以弹掉, 答案+1.
最后做到根节点会剩一些一定合并的集合, 记得答案+1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <bits/stdc++.h>
using namespace std;
const int maxn = 1e+6 + 5;
struct edge { int to, nxt; }e[maxn << 1]; int ptr, lnk[maxn], dep[maxn], ans, dgr[maxn], rt, n, k; set<int> st;
inline void add(int bgn, int end) { e[++ptr] = (edge){end, lnk[bgn]}; lnk[bgn] = ptr; } int dfs(int x, int fa) { if(dgr[x] == 1) return 0; vector<int> s; for(int p = lnk[x]; p; p = e[p].nxt) { int y = e[p].to; if(y == fa) continue; int tmp = dfs(y, x); s.push_back(tmp + 1); } sort(s.begin(), s.end()); for(int i = int(s.size()) - 1; i >= 1; --i) { if(s[i] + s[i-1] <= k) break; ans++; s.pop_back(); }
return s.back(); }
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> k; for(int i = 1; i < n; ++i) { int u, v; cin >> u >> v; add(u, v); add(v, u); dgr[u]++; dgr[v]++; } for(int i = 1; i <= n; ++i) if(dgr[i] != 1) {rt = i; break;} dfs(rt, 0);
cout << ans + 1 << endl; return 0; }
|