AtCoder Beginner Contest 309
B - Rotate
难度: ⭐
题目大意
给定一个n*n的矩阵, 要求把矩阵的最外围按照顺时针转动一个数据, 输出转动后的矩阵;
解题思路
数据不大, 暴力即可;
神秘代码
#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 = 1e5 + 10;
int n, m;
char g[110][110];
bool f = false;
signed main(){
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> g[i][j];
}
}
int x = g[1][1];
for (int i = 1; i < n; i++) g[i][1] = g[i + 1][1];
for (int i = 1; i < n; i++) g[n][i] = g[n][i+1];
for (int i = n; i > 1; i--) g[i][n] = g[i - 1][n];
for (int i = n; i > 1; i--) g[1][i] = g[1][i-1];
g[1][2] = x;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout<< g[i][j];
}
cout << endl;
}
return 0;
}
C - Medicine
难度: ⭐
题目大意
小莫有一个吃药列表, 每个药物有两个参数a, b; 表示从吃a天, 每天吃b个; 给定一个数量k, 问第一次吃药数量小于等于k的的第几天;
解题思路
算是一个比较明显的二分, 需要注意的一点是二分的上限要大于最大的天数, 否则当k=0的时候会输出0, 而正确答案应该是最大天数+1;
神秘代码
#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 = 3e5 + 10;
int n, m;
int a[N], b[N];
bool f=false;
bool check(int u) {
int res=0;
for (int i = 1; i <= n; i++) {
if (a[i] >= u) res += b[i];
}
if (res <= m) return true;
else return false;
}
signed main(){
cin >> n >> m;
int maxn=0;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
maxn=max(maxn,a[i]);
}
int l = 1, r = maxn+10;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r=mid;
f=true;
}
else l = mid + 1;
}
if(f) cout<<l;
else cout<<0;
return 0;
}
D - Add One Edge
难度: ⭐⭐
题目大意
现在有n1+n2个节点和m条边, 这些节点被分到节点1和节点n1+n2的两个连通块中, 节点1的连通块有n1个节点, 节点n1+n2的连通块有n2个节点; 现在我们在两个连通块中各找一个节点, 在这两个节点之间连一条边; 问从节点1到节点n1+n2的最短路径(边的个数)的最大值为多少;
解题思路
该题的题面多少有点坑, 很容易把人的思考重心放到该怎么确定两边要连通的点, 但是思考过后才发现一个样; 我们只需要两边各进行一次最短路, 找到两个连通块中的点到各自中心点的距离, 然后找到两边各自距离中心点最远的点, 这两个距离相加后+1就是答案了;
神秘代码
#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 = 3e5 + 10;
typedef pair<int, int> PII;
int n1,n2, m;
vector<int> v[N];
int d1[N], d2[N];
bool st[N];
int dijkstra(int u,int d[]) {
int res = 0;
d[u]=0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({ 0,u });
while (heap.size()) {
auto t = heap.top();
heap.pop();
int id = t.second;
res = max(res, d[id]);
if (st[id]) {
continue;
}
st[id] = true;
for (int j : v[id]) {
if (d[j] > d[id] + 1) {
d[j] = d[id] + 1;
heap.push({ d[j],j });
}
}
}
return res;
}
signed main() {
cin >> n1 >> n2 >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
memset(d1, 0x3f, sizeof d1);
memset(d2, 0x3f, sizeof d2);
int a=dijkstra(1,d1);
int b=dijkstra(n1 + n2,d2);
cout << a + b + 1;
return 0;
}
E - Family and Insurance
难度: ⭐⭐⭐
题目大意
一个家族有n个人, 其中1是辈分最大的, 给出2~n中每人的父亲是谁(1是辈分最大的), 组成一个家庭族谱; 该家族一共录有m份保险, 每份保险有两个参数a, b; a表示是给谁录的保险, b表示该保险同样可以作用于a往下的b代子孙; 问该家族中一共多少人有保险;
解题思路
保险个数和家族人数都是3e5, 所以我想着每遍历一个保险就去操作一遍家族成员就很费时间的; 所以我想着能不能在遍历保险的时候只打标记, 最后只遍历一次家族成员; 在尝试过后发现是可以的; 我们用一个数组作为标记st[a] = b, 表示成员a上有保险, 并且其子孙b代都可以享用; 打完标记后用bfs遍历家族成员, 遇到有保险的成员就可以把保险传递下去, st[c] = st[a] - 1; 这样就需要遍历一遍家庭成员就可以得到所有有保险的人员个数;
神秘代码
#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 = 3e5 + 10, mod = 998244353;
int n, m, k, res;
int d[N];
vector<int> v[N];
void bfs(){
int idx = 0;
queue<int> q;
q.push(1);
while(q.size()){
int t = q.front();
q.pop();
if(d[t]) idx++;
for(int x : v[t]){
d[x] = max(d[x], d[t] - 1);
q.push(x);
}
}
cout << idx;
}
signed main(){
cin >> n >> m;
for(int i = 2; i <= n; i++){
int a;
cin >> a;
v[a].push_back(i);
}
for(int i = 1; i <= m; i++){
int a, b;
cin >> a >> b;
d[a] = max(d[a], b + 1);
}
bfs();
return 0;
}
F - Box in Box
难度: ⭐⭐⭐⭐
题目大意
给定n个盒子的长宽高(a, b, c), 问是否存在一个盒子的长宽高严格大于另一个盒子的长宽高; 盒子可以旋转, 也就是说长宽高可以相互转换;
解题思路
由于本题问的是存在性, 所以并不需要三位偏序; 我们可以先把所有盒子按照a从小到大排序; 这样我们就只需要考虑b和c就可以了; 我们可以把b离散化为1 ~ n; 然后把b作为c的下标; 因为我们遍历到第i个盒子时, 第i个盒子的a一定大于前i - 1个盒子的a, 如果cx > cy 并且 x > y (x和y时bi和bj离散化后的值), 则说明在bi大于bj的同时ci也也大于cj; 这样就找到了一个长宽高都严格大于另一个盒子的盒子;
当我们遍历到第i个盒子时, 我们就去找前x(bi的离散值)个中最小的c是多少, 只要ci大于这个c就说明找到了; 对此我们可以用树状数组来维护离散化后前x个里面c的最小值;
里面细节比较多: 一是离散化时相同的b的离散值也得是相同的, 因为比较时b的离散值也要严格大于才行, 所以树状数组的长度不一定是n; 二是两个盒子的a可能也是相同的, 所以我们要把连续的具有相同a的盒子都处理完之后才能再去一一用它们的b和c去更新树状数组; 如果不这样, 即使找到了满足条件的b和c, a也有可能是相同的;
神秘代码
#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 = 1e6 + 10;
int n, m, idx;
int t[N];
map<int, int> pos;
struct Node{
int a, b, c;
}stu[N];
int lowbit(int x){
return x & -x;
}
bool cmp1(Node a, Node b){
return a.a < b.a;
}
bool cmp2(Node a, Node b){
return a.b < b.b;
}
void modify(int u, int x){
for(int i = u; i <= idx; i += lowbit(i)){
t[i] = min(t[i], x);
}
}
int find(int x){
int minn = 1e9 + 10;
for(int i = x; i > 0; i -= lowbit(i)){
minn = min(minn, t[i]);
}
return minn;
}
signed main(){
cin >> n;
for(int i = 0; i <= n; i++) t[i] = 1e9 + 10;
for(int i = 1; i <= n; i++){
int a, b, c;
cin >> a >> b >> c;
if(a > b) swap(a, b);
if(a > c) swap(a, c);
if(b > c) swap(b, c);
stu[i] = {a, b, c};
}
sort(stu + 1, stu + 1 + n, cmp2);
for(int i = 1;i <= n; i++){
if(stu[i].b != stu[i - 1].b) pos[stu[i].b] = ++idx;
}
for(int i = 1; i <= n; i++){
stu[i].b = pos[stu[i].b];
}
sort(stu + 1, stu + 1 + n, cmp1);
for(int i = 1; i <= n; i++){
int l = i, r = i;
while(stu[l].a == stu[r + 1].a) r++;
for(int j = l; j <= r; j++){
int x = find(stu[j].b - 1);
if(x < stu[j].c){
cout << "Yes";
return 0;
}
}
for(int j = l; j <= r; j++){
modify(stu[j].b, stu[j].c);
}
i = r;
}
cout << "No";
return 0;
}