Codeforces Round #706 (Div. 2) A B C
仍然只过了三题,但是速度慢了,肯定掉分了.
ABC的思维能力总是够的,但是不够快,准.
这次的B上手就用模拟,其实稍微想一下就发现会TLE,直到做完了C再看才发现需要特判剪枝.
不很常用的数据结构复杂度(map,set之类)也不清楚,甚至写到一半去百度了.
最后B还是得打草稿找规律,可能除了A题最好都要写写画画.
中途还去看了D,其实不如把这个时间花在B上面,经验表明自己正常情况下总是有能力做出B的.
不好总结,多练练吧,简单题还是太慢了.
A
很容易发现R(a2)+R(a1)=R(a1+a2),那么可以简化为s=a1+a2+R(a1),则只需判断是否存在长度不小于k的字符串a1即可.
注意判断一下s长度为偶数的情况,处理好细节.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <string> using namespace std; int n, k; string s; void solve(){ scanf("%d%d", &n, &k); cin >> s; int pl = 0, pr = n - 1; while(s[pl] == s[pr] && pl < pr){pl++, pr--;} if(pl == 0 && k == 0) {puts("YES");return;} if(n & 1) if(k <= pl) puts("YES"); else puts("NO"); else if(k < pl) puts("YES"); else puts("NO"); } int main(){ int t; scanf("%d", &t); while(t--) solve(); return 0; }
B
使用unordered_map记录数字(因为达到了1e9)是否已经存在,每当把一个原先不存在的数字标记为存在时将答案加一.
现在注意这样一个现象:n<=1e5,k<=1e9.
k在极端情况下直接使得模拟超时,说明你得找点歪路子.
假设集合进行去重升序排序,会发现,max为最右端元素,且mex在一个其左侧的位置,一般情况下期望他和max相距比较远.
现在把(max+mex)/2向上取整加入到这个序列中,很容易达成一种情况:max没有改变,mex也没有改变.一旦进入这种状态,之后不论进行多少次操作都不再对结果造成影响,此时break即可.
再仔细想一想,可知上述情况发生只需要在mex位于max左侧就可以了.
此外还有mex位于max右侧且相邻的情况,比如样例的{0,1,2}.这是一种很特殊的情况,容易看出来此后每次操作都一定会增加一个递增的新元素,所以只需要把答案加上剩下的操作数即可.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <unordered_map> using namespace std; unordered_map<int, int> m; // val, ct int n, k; void solve() { m.clear(); int ans = 0; int b = 0, a = 0; // a - mex, b - max scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); b = max(b, x); ans++; m[x]++; } for (int i = 0; i <= b + 1; i++) if (m[i] == 0) { a = i; break; } int last = -1; while (k--) { int x = (a + b - 1) / 2 + 1; if(x == last) break; else if(a == b + 1){ ans += k + 1; break; } if (m[x] == 0) ans++; m[x]++; b = max(b, x); while (m[a] != 0) a++; last = x; } printf("%d\n", ans); } int main() { int t; scanf("%d", &t); while (t--) solve(); return 0; }
C
这题比较有意思,证明猜想之后实现很简单.
注意到数值的符号并不会造成影响,所以干脆把所有点都移到其对应的正半轴上,现在任意矿工与任意钻石之间的连线都位于第一象限.
会自然而然地认为,不停地让截距最小的矿工取走截距最小的钻石可以得最优解,可以得到如下证明:
假设有矿工位于a1,a2,钻石位于b1,b2,如图,红线表示(a1,b1),(a2,b2)的组合,绿线表示(a2,b1),(a1,b2)的组合:
会发现红绿线构成了两个对顶的三角形,如果运用"三角形两边之和大于第三边"的定理,可得绿线长度之和大于红线.
这意味着一旦某些组合之间发生了交叉(绿线),那么总是可以交换他们来消除交叉(红线),这就粗略地证明了猜想的正确性.
这样做确实就AC了.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <cmath> using namespace std; int n; double x[100010], y[100010]; double calc(double x, double y){ return sqrt(x * x + y * y); } void solve(){ int p1 = 0, p2 = 0; scanf("%d", &n); for(int i = 1; i <= 2 * n; i++){ double a, b; scanf("%lf%lf", &a, &b); if(a == 0) y[++p2] = fabs(b); else x[++p1] = fabs(a); } sort(x + 1, x + 1 + n); sort(y + 1, y + 1 + n); double ans = 0; for(int i = 1; i <= n; i++) ans += calc(x[i], y[i]); printf("%.10f\n", ans); } int main(){ int t; scanf("%d", &t); while(t--) solve(); return 0; }
经常羡慕十几场就上橙红的人,我也打了十几局了,发现上蓝也没想象得那么容易.
不过有的人是有基础的,理所当然上分快,而另一些人上分比你还慢得多.
也看到一个在我前面几名的蓝名,掉了60分.
有句话说不要怕掉分,不停打就行了.
毕竟蓝名也会掉分,我这又算什么呢.