Codeforces Round #719 (Div. 3) (A~G)

Codeforces Round #719 (Div. 3)

A - Do Not Be Distracted!

题意

给定一个长度为 n n n的且只包含大写字母的字符串 s s s

问同一个字符是否只连续出现了一段

思路

直接依次遍历字符,利用map去标记已经出现的字符,检查新出现的字符是否被标记过即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
char s[MAXN];
map<char, int> mp;
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        mp.clear();
        int n, flag = 0;
        scanf("%d", &n);
        scanf("%s", s);
        int len = strlen(s);
        mp[s[0]] = 1;
        for (int i = 1; i < len; i++) {
            if (mp[s[i]] == 1 && s[i] != s[i - 1]) {
                flag = 1;
            }
            mp[s[i]] = 1;
        }
        if (flag == 1) {
            printf("NO\n");
        } else {
            printf("YES\n");
        }
    }
    return 0;
}

B - Ordinary Numbers

题意:

定义了一个数字是 o r d i n a r y ordinary ordinary的,当且仅当它每一位上的数字都相同

问从 1 1 1 n n n有多少个数字是 o r d i n a r y ordinary ordinary

思路:

知道要满足每一位都是相同的,那么这个每一位的数可以是1-9,直接暴力跑1-9的每一位的情况,如果小于 n n n就跳出即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ll t;
    scanf("%lld", &t);
    while (t--) {
        ll n, tot = 0;
        scanf("%lld", &n);
        for (ll i = 1; i <= 9; i++) {
            ll x = i;
            while (x <= n) {
                tot++;
                x = x * 10 + i;
            }
        }
        printf("%lld\n", tot);
    }
}

C - Not Adjacent Matrix

题意

需要构造一个 n ∗ n n * n nn的矩阵,使得相邻的两个数的差值 > 1 > 1 >1,并且1到 n 2 n^2 n2的数字只能在矩阵中出现一次。

思路

只有在 n = 2 n = 2 n=2的时候不存在答案

其余情况,可以先构造一个 1 − n 2 1- n^2 1n2的矩阵,从右往左依次排列的矩阵。

在这里插入图片描述

之后都可以每一个偶数列的数字将其整体上移一格。第一行移到最后一行,即可满足题意。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 10;
int a[MAXN][MAXN];
int b[MAXN][MAXN];
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n;
        scanf("%d", &n);
        int k = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                a[i][j] = k;
                k++;
            }
        }
        if (n == 1) {
            printf("%d\n", 1);
        } else if (n == 2) {
            printf("-1\n");
        } else {
            int k;
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (i % 2 == 0) {
                        if (j + 1 > n) {
                            k = 1;
                        } else {
                            k = j + 1;
                        }
                        b[j][i] = a[k][i];
                    } else {
                        b[j][i] = a[j][i];
                    }
                }
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    printf("%d ", a[i][j]);
                }
                printf("\n");
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    printf("%d ", b[i][j]);
                }
                printf("\n");
            }
        }
    }
    return 0;
}

D - Same Differences

题意

给定了一个数列{ $a $ }

询问存在多少个二元组 ( i , j ) (i,j) (i,j),满足 i < j i<j i<j a j − a i = j − i a_{j} - a_i = j-i ajai=ji

思路

a j − a i = j − i a_{j} - a_i = j-i ajai=ji 可以转换为 a j − j = a i − i a_{j} - j = a_i-i ajj=aii

按照顺序依次遍历每个位置的数

利用map存储数字出现的次数,用vector存储出现的不同的数字,之后遍历vector,这个数字出现了 x x x则可以组成 x ∗ ( x − 1 ) / 2 x*(x-1)/2 x(x1)/2个二元组,最后加起来即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 2e5 + 10;
ll a[MAXN];
vector<ll> v;
map<ll, ll> mp;
int main() {
    ll t;
    scanf("%lld", &t);
    while (t--) {
        v.clear();
        mp.clear();
        ll n;
        scanf("%lld", &n);
        for (ll i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            a[i] = a[i] - i;
            if (mp[a[i]] == 0) {
                v.push_back(a[i]);
            }
            mp[a[i]]++;
        }
        ll tot = 0;
        for (ll i = 0; i < v.size(); i++) {
            tot += ((mp[v[i]] * (mp[v[i]] - 1)) / 2);
        }
        printf("%lld\n", tot);
    }
    return 0;
}

E - Arranging The Sheep

题意

给定了一个字符串 s s s,只包含字符 . 和 *字符分别表示为空地和绵羊

每次操作可以将任意一只绵羊往左或者往右移动一格,只要目标位置存在并且为空地。

要求将所有的绵羊排列成一列(即所有绵羊之间都不能有空地)

问最小的操作数。

思路

先通过遍历一遍得到所有的绵羊数,得到最开始的位置右边的绵羊数。

再依次遍历每一位

如果当前位是绵羊,则左边的绵羊数加1,右边的绵羊数减1。

如果当前位是空地,那么左边和右边的绵羊哪边都至少能够整体移动一格,于是选择左边与右边绵羊数较少的绵羊整体向右移动一格。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 1e6 + 10;
char s[MAXN];
int main() {
    ll t;
    scanf("%lld", &t);
    while (t--) {
        ll n;
        scanf("%lld", &n);
        scanf("%s", s);
        ll l = 0, r = 0;
        for (ll i = 0; i < n; i++) {
            if (s[i] == '*') {
                r++;
            }
        }
        ll ans = 0;
        for (ll i = 0; i < n; i++) {
            if (s[i] == '*') {
                l++;
                r--;
            } else {
                ans += (min(l, r));
            }
        }
        printf("%lld\n", ans);
    }
}

F1 - Guess the K-th Zero (Easy version)

题意

现有一个长度为 n < = 2 ∗ 1 0 5 n <= 2*10^5 n<=2105的01序列, t = 1 t=1 t=1

每次你可以输出 ? ? ? l l l r r r来查询 [ l , r ] [l,r] [l,r]的区间和,最多查询20次

要求你找到第 k k k个0的下标是多少(从1开始)

思路

直接利用二分维护一个可行区间 [ l , r ] [l,r] [l,r] 初始 l = 1 , r = n l=1,r=n l=1,r=n

每次查询 [ l , m i d ] [l,mid] [l,mid],区间的长度为 m i d − l + 1 mid-l+1 midl+1,查询的返回值 s u m sum sum即代表着区间中 1 1 1的个数

m i d − l + 1 − s u m mid-l+1-sum midl+1sum 即代表着区间内0的个数

如果 m i d − l + 1 − s u m > = k mid-l+1-sum >=k midl+1sum>=k ,那么表示第 k k k 0 0 0在区间 [ l , m i d ] [l,mid] [l,mid]区间内,此时递归 [ l , m i d ] [l,mid] [l,mid]

如果 m i d − l + 1 − s u m < k mid-l+1-sum<k midl+1sum<k,那么表示第 k k k 0 0 0在区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]区间内,此时递归 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]

直到 l = = r l==r l==r,输出即可,查询的小于20次的。

#include <bits/stdc++.h>
using namespace std;
void calc(int l, int r, int k) {
    if (l == r) {
        cout << "! " << l << endl;
        return;
    }
    int m = (l + r) / 2;
    cout << "? " << l << " " << m << endl;
    int sum;
    cin >> sum;
    if ((m - l + 1) - sum >= k) {
        calc(l, m, k);
    } else {
        calc(m + 1, r, k - (m - l + 1) + sum);
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, t, k;
    cin >> n >> t >> k;
    calc(1, n, k);
}

F2 - Guess the K-th Zero (Hard version)

题意

现有一个长度为 n < = 2 ∗ 1 0 5 n <= 2*10^5 n<=2105的01序列, t = m i n ( n , 1 e 4 ) t=min(n,1e4) t=min(n,1e4)

每次你可以输出 ? ? ? l l l r r r来查询 [ l , r ] [l,r] [l,r]的区间和,最多查询20次

要求你找到第 k k k个0的下标是多少(从1开始)

思路

照用上一题的思路,与上题不同的是,我们不能重复访问,我们需要对其已经访问过的区间进行记忆化。

#include <bits/stdc++.h>
using namespace std;
map<pair<int, int>, int> cache;
void dec(int pos, int L, int R) {
    cache[{L, R}]--;
    if (L != R) {
        int M = (L + R) / 2;
        if (pos <= M)
            dec(pos, L, M);
        else
            dec(pos, M + 1, R);
    }
}
int main() {
    int n, cases;
    cin >> n >> cases;
    for (int i = 0; i < cases; i++) {
        int k;
        cin >> k;
        int L = 0, R = n - 1;
        while (L != R) {
            int M = (L + R) / 2;
            pair<int, int> range = make_pair(L, M);
            if (cache.count(range) == 0) {
                cout << "? " << range.first + 1 << " " << range.second + 1 << endl;
                cin >> cache[range];
                cache[range] = range.second - range.first + 1 - cache[range];
            }
            int value = cache[range];
            if (k <= value)
                R = M;
            else {
                k -= value;
                L = M + 1;
            }
        }
        cout << "! " << L + 1 << endl;
        dec(L, 0, n - 1);
    }
}

G - To Go Or Not To Go?

题意

给定一张 n ∗ m n*m nm的图,起点是 ( 1 , 1 ) (1,1) (1,1),终点是 ( n , m ) (n ,m ) (n,m)

每个点都有一个值,其中 − 1 -1 1表示不可以走, 0 0 0 表示可以走,其他值表示可以走并且存在一个花费为 a i + a j a_i+a_j ai+aj的传送器。

行走只能是上下左右四个方向,每走一步的花费为 w w w

使用传送器可以立即传送到任意一个其他的传送器,花费则为两个传送器上的值之和 a i + a j a_i+a_j ai+aj

问从起点到终点的最小花费,不存在则输出 − 1 -1 1

思路

从起点搜一次到每个可走到的点的最短距离存在 d i s 1 dis1 dis1

从终点搜一次到每个可走到的点的最短距离存在 d i s 2 dis2 dis2

由于可以任意传送,所以如果使用传送器的话肯定只使用一次

所以 d i s 1 [ i ] [ j ] + a [ i ] [ j ] dis1[i][j]+a[i][j] dis1[i][j]+a[i][j]就可以表示从起点走到 ( i , j ) (i,j) (i,j)并使用了这个传送器的花费(如果存在传送器)

同理 d i s 2 [ i ] [ j ] + a [ i ] [ j ] dis2[i][j]+a[i][j] dis2[i][j]+a[i][j]就可以表示从 ( i , j ) (i,j) (i,j)走到终点并使用了这个传送器的花费(如果存在传送器)

维护上面两个的最小值,相加后再与不使用任何传送器的情况(即 d i s 1 [ n ] [ m ] dis1[n][m] dis1[n][m]或者 d i s 2 [ 1 ] [ 1 ] dis2[1][1] dis2[1][1])

注意不存在的情况输出 − 1 -1 1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 3e3 + 10;
const ll INF = 1e18 + 10;
ll mp[MAXN][MAXN];
ll dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
ll dis1[MAXN][MAXN];
ll dis2[MAXN][MAXN];
ll n, m, w;
bool check(ll x, ll y) {
    if (x >= 1 && x <= n && y >= 1 && y <= m) {
        return true;
    }
    return false;
}
void bfs(ll stx, ll sty, ll dis[MAXN][MAXN]) {
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= m; j++) {
            dis[i][j] = INF;
        }
    }
    dis[stx][sty] = 0;
    queue<pair<ll, ll>> q;
    q.push(pair<ll, ll>(stx, sty));
    while (!q.empty()) {
        pair<ll, ll> p = q.front();
        q.pop();
        ll x = p.first;
        ll y = p.second;
        for (ll i = 0; i < 4; i++) {
            ll px = dir[i][0] + x;
            ll py = dir[i][1] + y;
            if (check(px, py) && mp[px][py] != -1 && dis[px][py] > dis[x][y] + w) {
                dis[px][py] = dis[x][y] + w;
                q.push(pair<ll, ll>(px, py));
            }
        }
    }
}
int main() {
    scanf("%lld %lld %lld", &n, &m, &w);
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= m; j++) {
            scanf("%lld", &mp[i][j]);
        }
    }
    bfs(1, 1, dis1);
    bfs(n, m, dis2);
    ll ans1 = INF;
    ll ans2 = INF;
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= m; j++) {
            if (mp[i][j] <= 0) {
                continue;
            }
            ans1 = min(ans1, dis1[i][j] + mp[i][j]);
            ans2 = min(ans2, dis2[i][j] + mp[i][j]);
        }
    }
    ll ans = min(ans1 + ans2, dis1[n][m]);
    if (ans >= INF) {
        printf("-1\n");
    } else {
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kunyuwan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值