Loading

AtCoder Beginner Contest 304




B - Subscribers

题目大意

给定一个数, 只保留前三位数, 其他位数变为0; 若不足三位则直接输出原数;

解题思路

签到题不多嗦了;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=110;
int n;
signed main() {
    string s;
    cin >> s;
    int len = s.size();
    if (len < 4) cout << s;
    else {
        for (int i = 0; i < len; i++) {
            if (i >= 3) cout << 0;
            else cout << s[i];
        }
    }
    return 0;
}




C - Virus

题目大意

一个二维坐标系上有n个人, 给定他们的坐标; 其中1号以及被感染了, 与其欧式距离小于等于m的人都会被感染; 最后输出每个人的感染情况;

解题思路

用个bfs遍历就好了;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e3 + 10;
typedef pair<int, int> PII;
int n;
double m;
int x[N], y[N];
bool st[N];
void bfs(){
    queue<int> q;
    st[1] = true;
    q.push(1);
    while(q.size()){
        int t = q.front();
        q.pop();
        for(int i = 1; i <= n; i++){
            if(st[i]) continue;
            int d = (x[i] - x[t]) * (x[i] - x[t]) + (y[i] - y[t]) * (y[i] - y[t]);
            double dis = sqrt(d);
            if(m >= dis){
                st[i] = true;
                q.push(i);
            }
        }
    }
}
signed main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> x[i] >> y[i];
    }
    bfs();
    for(int i = 1; i <= n; i++){
        if(st[i]) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}




D - A Piece of Cake

题目大意

给定一个长宽为n和m的矩形蛋糕, 蛋糕上有k个草莓; 然后我们要对着蛋糕竖着切a刀, 横着切b刀; 每一刀都给出其对应在x轴或y轴上的坐标; 注意切的时候不会切到有草莓的那一行或列; 并且也不会切边缘的行和列, 而草莓也不会在边缘的行和列上; 切完之后, 找到所有蛋糕块里面含有草莓数量最少和最多的数量, 并输出这两个数;

解题思路

这个题的长和宽数据比较大, 而且a和b也都是1e5级别的; 所以我们不能从矩阵或者每块蛋糕的角度出发; 而草莓数量也是1e5; 所有我们可以从每个草莓下手; 我们可以遍历所有草莓, 然后找到其所在的蛋糕块, 然后更新该蛋糕块上草莓的数量, 这个我们可以用map来完成; 对于怎么找对应的蛋糕块, 我们可以用二分查找小于草莓坐标的坐标最大的横竖两刀, 而这两刀的交点就是该蛋糕块的左上角, 我们可以用它来代表该蛋糕块; 如果map里的蛋糕块数量小于所有的蛋糕块数量((a+1) * (b+1)), 那说明有蛋糕块上没有草莓, 故最小值为0;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
int n, m;
int w, h, c, r;
int a[N], b[N], C[N], R[N];
map<PII, int> mp;
signed main() {
    cin >> w >> h >> n;
    for(int i = 1; i <= n; i++){
        cin >> a[i] >> b[i];
    }
    cin >> r;
    for(int i = 1; i <= r; i++) cin >> R[i];
    cin >> c;
    for(int i = 1; i <= c; i++) cin >> C[i];
    for(int i = 1; i <= n; i++) {
        int r1 = lower_bound(R, R + 1 + r, a[i]) - R;
        int c1 = lower_bound(C, C + 1 + c, b[i]) - C;
        r1--, c1--;
        mp[{r1, c1}]++;
    }
    int maxn = 0, minn = 1e9;
    for(auto t : mp){
        maxn = max(maxn, t.second);
        minn = min(minn, t.second);
    }
    if(mp.size() != (r + 1) * (c + 1)) minn = 0;
    cout << minn << ' ' << maxn;
    return 0;
}




E - Good Graph

题目大意

给定一个无向图, 有n个点和m条边并给出这m条边, 再给出k组{ xi, yi } ( i = 1, 2, 3...k), 如果所有的xi和yi之间都没有边相连, 那么这个无向图就是合法的; 现在再给出q组{ xj, yj } ( j = 1, 2, 3...q), 每一组就是一次询问, 问如果把当前的xj和yj相连, 那此时无向图是否合法;
注意可能存在重边或自环; 而且相连不一定是直接相连, 1-2-5这种情况也算1和5相连

解题思路

很明显的一个并查集问题, 初识情况就是给了许多个连通块; 然后给出k组限制, 一开始我还在想, k和q都是1e5级别的, 肯定不能去一个个查; 所以不要去关注每个点, 只需要看每个连通块就行; 于是我们可以把k的限制看成是规定了某些连通块之间不能相连, 而不是点之间不能相连; 我们可以用set把不能相连的连通块存起来, 方便后期查找; 对于q组询问, 我们只需要看看给出的两个点所在的连通块是否在set里面存着, 如果没有则就是合法的;

神秘代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+10;
int n,m,c,d;
int p[N];
set<PII> s;
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)  p[i] = i;
    for (int i = 1; i <= m; i++) {
        int a, b;
        cin >> a >> b;
        if (find(a) != find(b))  p[find(b)] = find(a);
    }
    cin >> c;
    for (int i = 1; i <= c; i++) {
        int a, b;
        cin >> a >> b;
        s.insert({ find(a),find(b)});
    }
    cin >> d;
    for (int i = 1; i <= d; i++) {
        int a, b;
        cin >> a >> b;
        if ((s.count({ find(a),find(b) })) || (s.count({ find(b),find(a) }))) {
            cout << "No" << endl;
        }
        else cout << "Yes" << endl;
    }
    return 0;
}




F - Shift Table

题目大意

小莫和安姐在同一家便利店打工, 给定一个长度为n的字符串代表小莫有一个n天的日程表, 第i个字符为'#'表示小莫第i天要值班, 如果是' . '则是休息; 而安姐也要指定一个日程表, 有如下要求
一是小莫休息的日期, 安姐必须要去值班;
二是安姐会取n的一个因数m(不包括n), 并且让这n天的日程表是以m为周期循环的;
问安姐的日程表一共有多少种可能, 结果对998244353取模;

解题思路

一开始我们可以先把n的所有因数m存起来作为循环节的长度; 然后把字符串里所有' . '的位置p也存起来, 这是已经固定的安排, 在后续操作中我们可以把所有的p通过对m取余来聚集到同一个循环节里进行操作; 在一个长度为a的循环节里, 如果已经有b个' . '; 那么对于剩下的(a-b)个日期里我们有2的(a-b)次方个选择方案;
注意, 对于我们选择的循环节长度a, 它的所有方案中也包括了a的因数作为循环节长度时的方案; 比如a=6时的方案里面就存在a=1, a=2和a=3的所有方案, 所以为了避免重复必须要减去所有a的因数的方案; 对此我们可以用map来存储所有长度的方案;
二是注意对减法运算进行取模时记得要+mod之后再取模, 防止有负数; 因为这个debug了好久...
后来才知道这其实是容斥原理的思想: 求满足s1, s2, s3三个条件其中一个的方案数, 那么就可以求s1∪s2∪s3 = s1 + s2 + s3 - s1∩s2 - s1∩s3 - s2∩s3 + s1∩s2∩s3;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 998244353;
int n, m, res;
vector<int> v;
map<int, int> mp;
int p[N];
int qmid(int a, int b){
    int res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
signed main() {
    string s;
    cin >> n >> s;
    for(int i = 1; i < n; i++) { 
    // 找因数可以用试除法, 但是这里复杂度要求没那么高, 用不用都行
        if(n % i == 0) v.push_back(i);
    }
    for(int i = 0; i < v.size(); i++) {
        int x = v[i];
        memset(p, 0, sizeof p);
        int num = 0;
        for(int j = 0; j < s.size(); j++) {
            if(s[j] == '.') {
                if(!p[(j + 1) % x]) num++;
                p[(j + 1) % x]++;
            }
        }
        mp[x] = qmid(2, x - num) % mod;
        for(int j = 1; j < x; j++){
            if(x % j == 0){
                mp[x] = (mp[x] - mp[j] + mod) % mod;
            }
        }
        res = (res + mp[x]) % mod;
    }
    cout << res;
    return 0;
}
posted @ 2023-06-20 13:04  mostimali  阅读(27)  评论(0编辑  收藏  举报