AtCoder Beginner Contest 351
B - Spot the Difference
难度: ⭐
题目大意
给定两个矩阵, 找不同
解题思路
数据很小, 暴力就行;
神秘代码
#include<bits/stdc++.h>
#define int unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 998244353, inf = 131;
typedef pair<int, int> PII;
int n, m;
char g1[110][110], g2[110][110];
signed main(){
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g1[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g2[i][j];
if(g2[i][j] != g1[i][j]){
cout << i << ' ' << j;
}
}
}
return 0;
}
C - Merge the balls
难度: ⭐⭐
题目大意
现在有一个空序列, 进行n次操作; 每次操作会把一个数字球x放到序列右端, 然后开始检查: 如果最右边的球和次右边的球数字相同, 那么就可以取出这两个球, 并新加入一个数字球为(2 * x); 并且加入后可以继续检查; 最后输出序列中还有几个球
解题思路
用一个堆来维护一下就行;
神秘代码
#include<bits/stdc++.h>
#define int unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 998244353, inf = 131;
typedef pair<int, int> PII;
int n, m;
vector<int> v;
signed main(){
cin >> n;
for(int i = 1; i <= n; i++){
int x;
cin >> x;
v.push_back(x);
while(v.size() > 1){
int a = v.back();
v.pop_back();
int b = v.back();
v.pop_back();
if(a == b){
v.push_back(a + 1);
}
else{
v.push_back(b);
v.push_back(a);
break;
}
}
}
cout << v.size();
return 0;
}
D - Jump Distance Sum
难度: ⭐⭐⭐
题目大意
现有一个n * n的网格, 其中有若干个磁铁; 小莫身穿铁衣在网格中行走, 当他走到与磁铁相邻的位置(上下左右)时便会无法移动; 定义坐标(x, y)的自由度: 即从(x, y)为起点, 所能到达的网格数(包括起点);
解题思路
根据题意, 因为到达磁铁相邻位置时便无法移动, 可以理解是遇到了空气墙; 所以可以看成是磁铁和空气墙把整个网格分成了连通块, 题目问的就是格子数量最多的连通块的格子数; 我们可以对格子进行染色; 其中比较特殊的是, 磁铁相邻的位置是可以被归属于多个连通块的, 所以可以被染成多种颜色, 而其他的空格子在被染上颜色后就不能再被染成别的颜色了, 当然, 既然已经被染上颜色, 那么其他颜色的格子也不会遍历到他, 所以不用担心;
神秘代码
#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 = 1e3 + 10, mod = 998244353, inf = 131;
typedef pair<int, int> PII;
int n, m, idx;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
char g[N][N];
bool _is[N][N];
int p[N][N];
int num[N * N];
void bfs(int a, int b){
queue<PII> q;
q.push({a, b});
p[a][b] = idx;
while(q.size()){
PII t = q.front();
q.pop();
num[idx]++;
for(int i = 0; i < 4; i++){
int x = t.first + dx[i], y = t.second + dy[i];
if(x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == '.'){
if(_is[x][y]){
if(p[x][y] != idx){
p[x][y] = idx;
num[idx]++;
}
}
else{
if(!p[x][y]){
q.push({x, y});
p[x][y] = idx;
}
}
}
}
}
}
signed main(){
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] == '#'){
_is[i - 1][j] = true;
_is[i + 1][j] = true;
_is[i][j - 1] = true;
_is[i][j + 1] = true;
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(p[i][j] == 0 && g[i][j] == '.' && !_is[i][j]){
idx++;
bfs(i, j);
}
}
}
int res = 1;
for(int i = 1; i <= idx; i++) res = max(res, num[i]);
cout << res;
return 0;
}
E - Jump Distance Sum
难度: ⭐⭐⭐⭐
题目大意
在一个平面上有n个物品Pi, 给定他们的坐标; 小莫每次可以从(x, y)移动到(x + 1, y + 1)或(x + 1, y - 1)或(x - 1, y + 1)或(x - 1, y - 1); 定义两个物品A, B之间距离dist(A, B)为小莫从A到B所需要的移动次数; 如果无法到达就定为0;
计算 $\displaystyle\sum_{i=1}{N-1}\displaystyle\sum_{j=i+1}N \text{dist}(P_i, P_j)$.
解题思路
因为斜着走不好处理, 那么我们可以把坐标系进行旋转45°, 让小莫的移动方向变为上下左右; 同时旋转后根据数学公式, 我们可以把原坐标(x, y)改为现坐标(x + y, x - y), 并且小莫移动的步幅变为2, 这样就和之前的图等效了, 但是因为当前步幅为2, 所以最后结果要除以2;
这样我们就是把任意两点之间的曼哈顿距离进行求和, 暴力是O(n2); 但是曼哈顿距离我们可以通过把x和y分开来处理; 以x为例: 处理时先从小到大进行排序, 对于其中第i个xi, 因为只有前i - 1个坐标会到达xi, 所以xi对答案的贡献就是(i - 1) * xi - sum; sum即为前i - 1个x的和;
另外还需要判断是否可以到达, 通过观察可以发现, 对于坐标(x1, y1)和(x2, y2), 如果x1 + y1和x2 + y2对2取余相同, 那么他们就可以相互到达; 所以我们可以把所有坐标分成两拨来处理;
神秘代码
#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 = 4e5 + 10, mod = 998244353, inf = 131;
typedef pair<int, int> PII;
int n, m, idx;
struct node{
int x, y;
}p[N];
vector<int> X[2], Y[2];
int check(vector<int> &v){
sort(v.begin(), v.end());
int sum = 0;
int res = 0;
if(v.size()) sum = v[0];
for(int i = 1; i < v.size(); i++){
res = res + i * v[i] - sum;
sum += v[i];
}
return res;
}
signed main(){
cin >> n;
for(int i = 1; i <= n; i++){
int a, b;
cin >> a >> b;
p[i] = {a + b, a - b};
if((a + b) % 2){
X[1].push_back(a + b);
Y[1].push_back(a - b);
}
else{
X[0].push_back(a + b);
Y[0].push_back(a - b);
}
}
int res = 0;
for(int i = 0; i <= 1; i++){
res += check(X[i]);
res += check(Y[i]);
}
cout << res / 2;
return 0;
}
F - Double Sum
难度: ⭐⭐⭐(⭐)
题目大意
给定一个长度为n的数列A; 计算$\displaystyle \sum_{i=1}^N \sum_{j=i+1}^N \max(A_j - A_i, 0)$, 答案保证小于263;
解题思路
根据题意可以知道, 对于Ai, 我们要求它后面所有比他大的数的和, 暴力是O(n2)的复杂度; 转念一想, 我们新开一个数组P[x]表示数列A中值为x的总和是多少, P[x] = num * x, num为x的数量; 设数列中的最大值是maxn, 求所有比Ai大的数的和, 不就是求P[Ai]到P[maxn]的前缀和吗; 但是用普通的前缀和还是O(n2), 所以可以考虑用树状数组来做, 既然要求它后面所有比他大的数的和, 所以需要逆序来处理; 设num是Ai后面所有比他大的数的个数, sum是后面所有比他大的数的和, 那么Ai对答案的贡献就是sum - num * Ai;
另外因为Ai的范围是1e8, 所以开数组会MLE; 但是n为4e5, 所以可以进行离散化处理;
神秘代码
#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 = 4e5 + 10, mod = 998244353, inf = 131;
typedef pair<int, int> PII;
int n, m, idx;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int p[N], f[N];
int trs[N], trn[N];
map<int, int> mp1, mp2;
int lowbit(int x){
return x & -x;
}
void add(int x){
for(int i = x; i <= n; i += lowbit(i)){
trs[i] += mp2[x];
trn[i] ++;
}
}
int sum(int x){
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += trs[i];
}
return res;
}
int num(int x){
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += trn[i];
}
return res;
}
signed main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> p[i];
f[i] = p[i];
}
sort(f + 1, f + 1 + n);
for(int i = 1; i <= n; i++){
mp1[f[i]] = i;
mp2[i] = f[i];
}
int res = 0;
for(int i = n; i >= 1; i--){
int s1 = sum(n);
int s2 = sum(mp1[p[i]]);
int n1 = num(n);
int n2 = num(mp1[p[i]]);
res = res + (s1 - s2) - p[i] * (n1 - n2);
if(p[i]) add(mp1[p[i]]);
}
cout << res;
return 0;
}