Loading

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;
}
posted @ 2024-04-28 22:12  mostimali  阅读(70)  评论(0编辑  收藏  举报