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

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
", ans);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();

    return 0;
}
B

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
", ans);
}

int main(){
    int t;
    scanf("%d", &t);
    while(t--) solve();   

    return 0;
}
C

经常羡慕十几场就上橙红的人,我也打了十几局了,发现上蓝也没想象得那么容易.

不过有的人是有基础的,理所当然上分快,而另一些人上分比你还慢得多.

也看到一个在我前面几名的蓝名,掉了60分.

有句话说不要怕掉分,不停打就行了.

毕竟蓝名也会掉分,我这又算什么呢.

原文地址:https://www.cnblogs.com/Gaomez/p/14514958.html