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