AtCoder Beginner Contest 348
B - Farthest Point
难度: ⭐
题目大意
一个坐标系有x个点, 对于每个点找出距离其最远的点;
解题思路
数据很小, 暴力就行;
神秘代码
#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 = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, k;
PII p[N];
signed main(){
IOS;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> p[i].first >> p[i].second;
}
for(int i = 1; i <= n; i++){
int maxn = 0, pos = i;
for(int j = n; j >= 1; j--){
if(i == j) continue;
int dis = (p[i].first - p[j].first) * (p[i].first - p[j].first) + (p[j].second - p[i].second) * (p[j].second - p[i].second);
if(dis >= maxn){
maxn = dis;
pos = j;
}
}
cout << pos << endl;
}
return 0;
}
C - Colorful Beans
难度: ⭐⭐
题目大意
现在有若干个分组, 每个分组里都有若干个数值; 找出所有分组的最小值中的最大值;
解题思路
也是打暴力;
神秘代码
#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 = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, k;
map<int, int> mp;
signed main(){
IOS;
cin >> n;
for(int i = 1; i <= n; i++){
int a, b;
cin >> a >> b;
mp[b] = mp[b] ? min(mp[b], a) : a;
}
int res = 0;
for(auto t : mp){
res = max(res, t.second);
}
cout << res;
return 0;
}
D - Medicines on Grid
难度: ⭐⭐⭐
题目大意
给定一个网格, 确定起点和终点; 网络中某些位置是障碍物; 小莫从起点开始出发, 每上下左右走一步都会消耗一个能量, 初始的能量为0; 网格中的某些位置可以补充能量, 形式为(x, y, a)在(x, y)位置可以将小莫的能量补充到a; 每个位置的补充只能用一次;
解题思路
因为能量的补充形式是补充到a, 并非补充a; 所以我们可以贪心地想只要到达(x, y)时自身的能量小于a, 那么直接使用就可以了; 而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 = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m, k;
int sx, sy, ex, ey;
char g[250][250];
int st[250][250], eng[250][250];
bool est[250][250];
struct node{
int x, y, e;
};
int idx = 0;
bool bfs(){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
st[i][j] = -1;
}
}
queue<node> q;
if(!eng[sx][sy]) return false;
q.push({sx, sy, eng[sx][sy]});
st[sx][sy] = eng[sx][sy];
while(q.size()){
node t = q.front();
q.pop();
if(t.x == ex && t.y == ey) return true;
if(!t.e) continue;
for(int i = 0; i < 4; i++){
int x = t.x + dx[i], y = t.y + dy[i];
if(x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] != '#'){
int e = t.e - 1;
if(eng[x][y] && !est[x][y]){
if(eng[x][y] > e){
e = eng[x][y];
est[x][y] = true;
}
}
if(e > st[x][y]){
st[x][y] = e;
q.push({x, y, e});
}
}
}
}
return false;
}
signed main(){
IOS;
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> g[i][j];
if(g[i][j] == 'S'){
sx = i, sy = j;
}
else if(g[i][j] == 'T'){
ex = i, ey = j;
}
}
}
cin >> k;
for(int i = 1; i <= k; i++){
int a, b, c;
cin >> a >> b >> c;
eng[a][b] = c;
}
if(bfs()){
cout << "Yes";
}
else cout << "No";
return 0;
}
E - Minimize Sum of Distances
难度: ⭐⭐⭐⭐
题目大意
给定一个长的为n的数值C; 现在有一颗节点个数为n的树; 设函数d(a, b)表示节点a和b之间最短路径的边数; 设 $\displaystyle f(x) = \sum_{i=1}^{N} (C_i \times d(x, i))$. 找到$\displaystyle \min_{1 \leq v \leq N} f(v)$.
解题思路
题解说这个是换根dp, 当时是纯当是图论做的, 不过思路和换根dp基本一致; 因为数据范围是1e5, 所以肯定没法暴力; 考虑O(n)的做法就只能考虑相邻两个节点之间转换时f(x)的变化规律; 我们设p[u]表示为以u为根节点的子树中所有Ci的和, len[x]表示节点x到此时目标节点之间边的个数; 设节点v是u的子节点之一, 我们需要用f(u)来求f(v); 转移后, 以v为根节点的子树中的点的len[i]都会减一, 其对f(v)的影响就是f(u) - p[v]; 相应的, 除了该子树中的点, 其他点的len[i]都会加一, 这个其他点包括两部分, 一是节点u的其他子节点的子树, 可以用p[u] - p[v]表示; 二是节点u之前的那些节点, 这个在dfs时用个变量保存一下, 比如ls; 那么最终f(v) = f(u) + (p[u] - p[v]) + ls - p[v];
神秘代码
#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 = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m, res;
int l, r;
vector<int> v[N];
int p[N], f[N];
int ini(int u, int fa, int num){
int sum = 0;
for(int x : v[u]){
if(x == fa) continue;
r += p[x];
res += p[x] * num;
f[u] += ini(x, u, num + 1);
}
return f[u];
}
void solve(int u, int fa, int ls){
for(int x : v[u]){
if(x == fa) continue;
res = min(res, res + ls + f[u] - f[x] - f[x]);
solve(x, u, ls + f[u] - f[x]);
}
}
signed main(){
IOS;
cin >> n;
for(int i = 1; i < n; i++){
int a, b;
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
for(int i = 1; i <= n; i++){
cin >> p[i];
f[i] = p[i];
}
ini(1, -1, 1);
solve(1, -1, 0);
cout << res;
return 0;
}
F - Oddly Similar
难度: ⭐⭐⭐⭐
题目大意
现在有N个长度为M的数组, 对于两个数组, 如果他们在某一位置的数相同, 并且这样的数的个数是奇数个, 那么这两个数组被称为相似的; 请判断有多少对这样的(i, j)数组相似; 1 <= i < j <= M
解题思路
本题的暴力解法是O(n * n * m), 肯定会超时; 题解是用了bitset来进行优化, 用bitset可以快64倍, 也就是差不多1e7的水平; 开一个bitset的二维数组f[i][j]表示第i列数值为j的情况; 因为bitset可以看作是一个01字符串, 所以f[i][j][k] = 1表示第k行第i列的值为j; 也就是说, f[i][j]表示所有行在第i列的情况;
所以我们可以遍历所有行i, 对于每行我们遍历其每一列j, 并且逐列进行异或操作, 最终如果bitset中1的个数即为其第i行相似的行的个数; 又因为题目要求(i, j)中i < j; 所以我们可以先异或再赋值, 可以最后就不用去重了;
神秘代码
#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, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m, res;
int p[N][N];
bitset<N> x, f[N][N];
signed main(){
IOS;
cin >> n >> m;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> p[i][j];
}
}
for(int i = 0; i < n; i++){
x.reset();
for(int j = 0; j < m; j++){
x ^= f[j][p[i][j]];
f[j][p[i][j]][i] = 1;
}
res += x.count();
}
cout << res;
return 0;
}