AtCoder Beginner Contest 330 A~F

A.Counting Passes(暴力)

题意:

给定nnn个学生的分数,以及及格分xxx ,问多少人及格了。

分析:

暴力枚举,依次判断每个学生的分数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n, l;
    cin >> n >> l;
    int ans = 0;
    while (n--) {
        int x;
        cin >> x;
        ans += (x >= l);
    }
    cout << ans << endl;
    return 0;
}

B.Minimize Abs 1(数学)

题意:

回答 nnn 个问题,每个问题给定 a,l,ra,l,ra,l,r,问函数 f(x)=∣x−a∣f(x)=|x−a|f(x)=xa[l,r][l,r][l,r]的最小值。

分析:

全定义域下,最小值显然在 x=ax=ax=a 取得。绝对值函数图像是 VVV 型。
现在定义域限定在 [l,r][l,r][l,r],则分 a≤l,a≥r,l<a<ra \le l,a \ge r,l \lt a \lt ral,ar,l<a<r 三种情况分别讨论极值即可。即分别在x=l,x=r,x=ax=l,x=r,x=ax=l,x=r,x=a取得最小值。

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n, l, r;
    cin >> n >> l >> r;
    while (n--) {
        int a;
        cin >> a;
        if (a <= l)
            cout << l << ' ';
        else if (a >= r)
            cout << r << ' ';
        else
            cout << a << ' ';
    }
    return 0;
}

C.Minimize Abs 2(数学)

题意:

给定整数ddd,问函数 f(x,y)=∣x2+y2−d∣f(x,y)=|x^2+y^2−d|f(x,y)=x2+y2d的最小值。

分析:

枚举 xxx的取值,范围是[1,2e6][1,2e6][1,2e6],然后得 y2=abs(d−x∗x)y^2=abs(d−x∗x)y2=abs(dxx),分别取 y1=⌊y⌋,y2=⌈y⌉y_1=\lfloor\sqrt{y}\rfloor,y_2=\lceil\sqrt{y}\rceily1=y,y2=y,由于会有一正一负的情况(x2+(y1)2≤d,x2+(y2)2≥d)(x^2+(y_1)^2 \le d,x^2+(y_2)^2 \ge d)(x2+(y1)2d,x2+(y2)2d),取min(f(x,y1),f(x,y2))min(f(x,y1),f(x,y2))min(f(x,y1),f(x,y2)),对所有 xxx 取最小值即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
    LL d;
    cin >> d;
    int up = 2e6;
    LL ans = 1e18;
    for (int i = 0; i <= up; ++i)
    {
        LL x = 1ll * i * i;
        LL y = abs(d - x);
        LL y1 = floor(sqrt(y)), y2 = ceil(sqrt(y));
        ans = min({ans, abs(x + y1 * y1 - d), abs(x + y2 * y2 - d)});
    }
    cout << ans << endl;
    return 0;
}

D.Counting Ls(思维,枚举)

题意:

给定一个包含o或者x的二维矩阵。问所有满足下述条件的三元组下标数量。

  • 该三元组上的字符在矩阵中的位置各不相同,但是都是o
  • 该三元组中,其中两个字符在同一行,其中两个字符在同一列。

分析:

如果二维矩阵的一个位置的字符为o时,该字符可以作为中间点产生的贡献为(row[i]−1)∗(col[j]−1)(row[i]-1)*(col[j]-1)(row[i]1)(col[j]1),其中 row[i]row[i]row[i]表示该行o的个数,col[i]col[i]col[i]表示该列 o 的个数。累计这些答案计数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

string s[2005];
int row[2005], col[2005];

int main(){
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> s[i];
        for (int j = 0; j < n; j++) {
            if (s[i][j] == 'o') {
                row[i]++;
                col[j]++;
            }
        }
    }
    LL ans = 0;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j) {
            if (s[i][j] == 'o') {
                ans += 1ll * (row[i] - 1) * (col[j] - 1);
            }
        }
    cout << ans << endl;
    return 0;
}

E.Mex and Update(思维,数据结构)

题意:

给定一个数组aaa,进行如下操作。每次操作令ai=xa_i=xai=x。然后输出mex(a)mex(a)mex(a)

mex(a)mex(a)mex(a)表示数组aaa未出现的最小非负整数

分析:

考虑如何求出一个数组的mexmexmex,我们可以用一个mapmapmap表示数字iii的出现次数,那每次求 mexmexmex可以从小到大遍历该数组,找到第一个出现次数为000的下标即是答案。

但这复杂度可能会高达O(n)O(n)O(n),考虑更快速的方法,我们可以用setsetset储存mapmapmap中值为000(未出现的数)下标,这样setsetset中的最小值就是答案。

ai=xa_i=xai=x时,相当于把原来的aia_iai删掉,即mp[ai]mp[a_i]mp[ai]−−,然后把 xxx加进来,即mp[x]mp[x]mp[x]++,如果mp[ai]mp[a_i]mp[ai]变为000,则说明aia_iai没有出现,将其插入到setsetset中。同时mp[x]mp[x]mp[x]变为111,说明xxx出现了,从setsetset中删去。

这样就可以动态维护mexmexmex值,此时的每次操作的时间复杂度为O(logn)O(logn)O(logn)

hint:

  • 包含nnn个数的数组可能的mexmexmex0∼n0 \sim n0n

  • setsetset可以通过begin()begin()begin()方法取出头部元素的迭代器,然后通过解地址符*取出对应的值。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[200005];
map<int, int> mp;
set<int> mex;
int main(){
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
        mp[a[i]]++;
    }
    for (int i = 0; i <= n; ++i){
        if (mp[i] == 0){
            mex.insert(i);
        }
    }
    while (q--){
        int i, x;
        cin >> i >> x;
        if (mp[a[i]] == 1){
            mex.insert(a[i]);
        }
        mp[a[i]]--;
        if (mp[x] == 0){
            mex.erase(x);
        }
        mp[x]++;
        a[i] = x;
        cout << *mex.begin() << endl;
    }
}

F.Minimize Bounding Square(二分,前缀和)

题意:

二维平面上nnn个点,可进行最多 kkk 次操作,每次操作将一个点上下左右移动一格。点可以重叠。问进行若干次操作后,能将所有点覆盖的正方形的边长的最小值。

分析:

xxxyyy两个维度相互独立,我们可以分别考虑每个维度。

考虑一维情况下,如果我们固定覆盖的线段长度,会发现比较好做。注意到如果边长越大,我们需要的移动次数越少,可行的可能性就越高,相反,边长越小,需要移动的次数越多,可行的可能性就越低。

这里有一个单调性,因此我们可以二分最终的边长。问题转化为给定数轴上nnn个点,可以在数轴上放置一个区间,要求所有点到达区间内的最小移动距离,若该区间是一个点,则是一个经典问题,取中位数即可。

此题中不难发现最优选法区间的左端点或者右端点一定是其中某个点,可以枚举所有情况然后利用二分与前缀和去优化计算距离

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL k;
const int maxn = 2e5 + 5;
LL x[maxn], y[maxn], sx[maxn], sy[maxn];
LL calc(LL *p, LL *s, int mid){
    LL res = 1e18;
    for (int i = 1; i <= n; i++){
        LL v = 1ll * i * p[i] - s[i];
        int R = p[i] + mid;
        int l = 1, r = n;
        while (l < r){
            int mid = l + r >> 1;
            if (p[mid] > R)
                r = mid;
            else
                l = mid + 1;
        }

        if (p[r] > R)
            v += s[n] - s[r - 1] - 1ll * (n - r + 1) * R;
        res = min(res, v);
    }

    for (int i = n; i; i--){
        LL v = s[n] - s[i - 1] - 1ll * (n - i + 1) * p[i];
        int L = p[i] - mid;
        int l = 1, r = n;
        while (l < r){
            int mid = l + r + 1 >> 1;
            if (p[mid] < L)
                l = mid;
            else
                r = mid - 1;
        }

        if (p[r] < L)
            v += 1ll * r * L - s[r];
        res = min(res, v);
    }

    return res;
}
LL check(LL value){
    LL v1 = calc(x, sx, value), v2 = calc(y, sy, value);
    return v1 + v2 <= k;//v1+v2<=k时返回1,否则返回0
}
int main(){
    cin >> n >> k;
    for (int i = 1; i <= n; i++){
        cin >> x[i] >> y[i];
    }
    sort(x + 1, x + 1 + n);
    sort(y + 1, y + 1 + n);
    for (int i = 1; i <= n; i++){
        sx[i] = sx[i - 1] + x[i];
        sy[i] = sy[i - 1] + y[i];
    }
    LL l = 0, r = 1e9;
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << r << endl;
    return 0;
}

学习交流

以下学习交流QQ群,群号: 546235402,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值