曾经做过的一些小水题, 总之我是不会做.
这是最后一篇, 第3篇.
Topcoder SRM 744(Div.1 + Div.2) 解题口胡报告 + 吐槽.
发现其实SRM的题没有很不可做. 不过感觉赛时肯定还是会爆负, 让我75min想到写完这些题是做不到.
Div.2 250 入门题, 把一个左开右闭区间分成三个左开右闭区间.
没写, 除一除加一加就好了.
Div.2 500 入门题, 问一个区间里有多少数是完全平方数且满足各数位的数字是波动的.
我居然天真地想数位DP
数据范围不大, 反正枚举平方数再检查是够的.
Div.2 1000 / Div.1 250 有一个$N \times M$矩阵, 格子$(i,j)$的权值是$\max{(i,j)} \mod{3}$. 求一个给定子矩阵的权值和. $N, M \le 10^9$.
什么嘛矩阵是从0开始存的?
转成二维前缀和. 就讨论一下对角线, 对角线两边, 和较大的那一维多出来的一块, 加起来就行.
ModularQuadrant.cpp:
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 #include <bits/stdc++.h> using namespace std ;typedef long long ll;const int summ[3 ] = {0 , 1 , 3 };const int val[3 ] = {3 , 1 , 2 };inline ll seq (int init, int count) { ll tail = (ll)init + 3l l * (count - 1 ); return (init + tail) * count / 2l l; } class ModularQuadrant {public : ll sum (int , int , int , int ) ; ll prefix (int , int ) ; }; ll ModularQuadrant::sum(int r1, int c1, int r2, int c2) { return ModularQuadrant::prefix(r2, c2) - ModularQuadrant::prefix(r2, c1 - 1 ) - ModularQuadrant::prefix(r1 - 1 , c2) + ModularQuadrant::prefix(r1 - 1 , c1 - 1 ); } ll ModularQuadrant::prefix(int r, int c) { if (r == -1 || c == -1 ) return 0 ; int lim = min(r, c) + 1 ; ll ang = (lim / 3 ) * 3 + summ[lim % 3 ]; ll tri = seq(1 , lim / 3 ) * 1l l + seq(2 , lim / 3 ) * 2l l + seq(0 , lim / 3 ) * 0 ; if (lim % 3 == 1 ) { tri += (lim - 1 ) * 0 ; } else if (lim % 3 == 2 ) { tri += 1l l * (lim - 1 ) + 0l l * (lim - 2 ); } tri *= 2l l; if (r == c) return tri + ang; ll ret = 0 ; int bgn = lim, eed = max(r, c), len = eed - bgn + 1 ; ret += (len / 3 ) * 1l l * lim + (len / 3 ) * 2l l * lim + (len / 3 ) * 0l l * lim; if (len % 3 == 1 ) { ret += 1l l * val[bgn % 3 ] * lim; } else if (len % 3 == 2 ) { ret += 1l l * val[bgn % 3 ] * lim + 1l l * val[(bgn + 1 ) % 3 ] * lim; } return ret + ang + tri; }
Div.1 500 给一个01矩阵, 每次可以操作一个$(i,j)$, 将$i$行和$j$列除了她自己以外的格子都取反.
问有没有可能把全部格子都变成1. $N, M \le 2000$.
这就是比较经典的套路.
首先观察一下操作, 发现相当于把行取反再把列取反. 这和我们见过的题相比多出的限制就是行列操作次数必须相等, 只要最后查一下就行了.
枚举第一行反或不反, 把后面的行都和她对齐, 然后看第一行还有哪些是0, 就要对列取反. 把次数统计起来.
UniformingMatrix.cpp:
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 #include <bits/stdc++.h> using namespace std ;const string YES = "Possible" ;const string NO = "Impossible" ;int line[200 ];class UniformingMatrix {public : string isPossible (vector <string > M) ; }; string UniformingMatrix::isPossible(vector <string > M) { int n = M.size(), m = M[0 ].size(); bool sol = true ; int row = 0 , col = 0 ; for (int i = 0 ; i < m; ++i) line[i] = M[0 ][i] - '0' ; for (int i = 1 ; i < n; ++i) { int lst = line[0 ] ^ (M[i][0 ] - '0' ); for (int j = 1 ; j < m; ++j) { if ((M[i][j] - '0' ) ^ line[j] != lst) { sol = false ; break ; } } if (sol == false ) break ; if (lst) row++; } if (sol) { for (int i = 0 ; i < m; ++i) if (!line[i]) col++; if (row == col) return YES; } for (int i = 0 ; i < m; ++i) line[i] = (1 ^ (M[0 ][i] - '0' )); row = 1 ; col = 0 ; for (int i = 1 ; i < n; ++i) { int lst = line[0 ] ^ (M[i][0 ] - '0' ); for (int j = 1 ; j < m; ++j) { if ((M[i][j] - '0' ) ^ line[j] != lst) { sol = false ; break ; } } if (sol == false ) break ; if (lst) row++; } if (sol) { for (int i = 0 ; i < m; ++i) if (!line[i]) col++; if (row == col) return YES; } return NO; }
Div.1 1000 给一颗有根树. 每个点有需求和花费. 给每个点赋值使得每个点到根的路径上的权值和不小于需求, 代价是值乘以点权.
最小化花费. $N \le 10^5$.
TC题正解总是用匪夷所思的简单做法我居然想要DP
自下而上地贪心考虑. 我们的目的首先是满足权值下限, 先不考虑祖先(后面会更新). 考虑子树中所有和当前节点路径上权值和为0(不包括端点)的节点. 做到这里时他们的要求肯定是满足的. 那么我们可以考虑, 如果减小他们的权值, 而在当前节点增加同样的权值, 如果子节点的费用和大于当前节点的费用, 这一步调整就是优的.
如何快速做这个过程呢? 可以用小根堆来维护各点权值. 初始先将儿子压入堆. 然后做操作. 当堆顶被减为0时, 把堆顶节点的堆和当前堆合并. 为了保证复杂度要启发式合并. (感觉左偏树也是可以的).
然后还有一些细节, 大概就是每次减小量的上界还有满足条件就退出之类的, 代码里都写了.
还有这鬼畜的加密读入
CoverTreePaths.cpp:
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 #include <bits/stdc++.h> using namespace std ;typedef long long ll;const int maxn = 1e+5 + 5 ;struct edge { int to, nxt; }e[maxn]; struct node { ll val; int id; node(ll v_ = 0 , int i_ = 0 ) : val(v_), id(i_) {} inline bool operator <(const node &rhs) const { return (val == rhs.val) ? (id > rhs.id) : (val > rhs.val); } }; int f[maxn], cs[maxn], nd[maxn];ll sumcs[maxn], sub[maxn]; int ptr, lnk[maxn];int v[maxn];priority_queue<node> q[maxn]; inline void add (int bgn, int end) { e[++ptr] = (edge){end, lnk[bgn]}; lnk[bgn] = ptr; } void merge (int frm, int to) { while (q[frm].size()) { node u = q[frm].top(); q[frm].pop(); u.val -= sub[frm]; u.val += sub[to]; q[to].push(u); } } class CoverTreePaths {public : ll minimumCost (int , vector <int >, vector <int >, vector <int >, vector <int >) ;}; ll CoverTreePaths::minimumCost(int n, vector <int > p, vector <int > d, vector <int > c, vector <int > params) { for (vector <int >::size_type i = 0 ; i < p.size(); ++i) { f[i + 1 ] = p[i]; add(f[i + 1 ], i + 1 ); } int bgn = p.size(); for (int i = bgn; i < n - 1 ; ++i) { p.push_back((1l l * params[0 ] * p[i - 1 ] + params[1 ]) % (i + 1 )); f[i + 1 ] = p[i]; add(f[i + 1 ], i + 1 ); } for (vector <int >::size_type i = 0 ; i < d.size(); ++i) { nd[i] = d[i]; } bgn = d.size(); for (int i = bgn; i < n; ++i) { d.push_back((1l l * params[2 ] * d[i - 1 ] + params[3 ]) % params[4 ] + 1 ); nd[i] = d[i]; } for (vector <int >::size_type i = 0 ; i < c.size(); ++i) { cs[i] = c[i]; } bgn = c.size(); for (int i = bgn; i < n; ++i) { c.push_back((1l l * params[5 ] * c[i - 1 ] + params[6 ]) % params[7 ] + 1 ); cs[i] = c[i]; } ll ans = 0 ; for (int k = n - 1 ; ~k; --k) { sumcs[k] = 0 ; sub[k] = 0 ; for (int p = lnk[k]; p; p = e[p].nxt) { int y = e[p].to; sumcs[k] += cs[y]; q[k].push(node(v[y], y)); } while (1 ) { ll delta = 0 ; if (!q[k].size()) { if (v[k] >= nd[k]) { break ; } else delta = nd[k] - v[k]; } else if (cs[k] < sumcs[k]) { delta = q[k].top().val - sub[k]; } else if (v[k] < nd[k]) { delta = min((ll)nd[k] - v[k], q[k].top().val - sub[k]); } else break ; ans += delta * cs[k]; ans -= delta * sumcs[k]; sub[k] += delta; v[k] += delta; vector <node> waitList; while (q[k].size() && q[k].top().val == sub[k]) { node u = q[k].top(); q[k].pop(); waitList.push_back(u); sumcs[k] -= cs[u.id]; } for (auto u : waitList) { sumcs[k] += sumcs[u.id]; if (q[u.id].size() <= q[k].size()) { merge(u.id, k); } else { merge(k, u.id); q[k] = q[u.id]; sub[k] = sub[u.id]; } } } } return ans; }
没啦.