竞赛讨论区 > 【题解】牛客小白月赛31
头像
DongResponse
编辑于 2021-01-11 10:48
+ 关注

【题解】牛客小白月赛31

A|B

假设 A=100100010,则满足 A+B=A|B 条件的B,在 A 中为1的位置必定为0,也就是 B=0xx0xxx0x,其中的 x 可为 0 或 1,因此只要数一下 A 裡面有几个位元是 0,算出 2 的次方就可以得到答案。

但是题目又规定 B <=X ,所以我们要将 X 分段处理,假设 X=1001000100,先找出最左边第一个出现的 1 为 1000000000,分出第一段数字 xxxxxxxxx,(不含上面的数)
也就是 0 ~ 111111111 (1000000000-1),再用这一段去找有几个位置在 A 中是 0。接下来再找出第二个出现的 1 为 1000000,而第二段数字为 1000xxxxxx,也就是 1000000000 ~ 1000111111 (1001000000-1),数字的最前面固定是 1000,一样找出后面的位置有几个在 A 中是 0。
要注意的是,如果这个位置在 X 和在 A 中同时为 1,则后面就不会再有答案。因为这个做法只能处理到 X-1,所以 X 要另外处理。
最后,因为题目要求要正整数,但这个做***包含 0,所以答案要再减一。

#include <bits/stdc++.h>

using namespace std;
int A[32];
int a, x, t;
int ans, c, bit, bit2;

int main() {
    for (int i = 0; i < 31; ++i)
        A[i] = pow(2, i);
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &a, &x);
        for (bit = (1 << 30), ans = 0; bit; bit >>= 1) {
            if ((bit & x) == 0) continue;
            for (bit2 = (bit >> 1), c = 0; bit2; bit2 >>= 1)
                if ((bit2 & a) == 0) c++;
            ans += A[c];
            if (bit & a) break;
        }
        if ((a | x) == (a + x)) ans++;
        printf("%d\n", ans - 1);
    }
    return 0;
}

A+B

按照题意模拟即可

#include <iostream>
#include <map>

using namespace std;

int n;
string a[5];
map<string, int> mp;
string num[11] = {"####.##.##.####",
                  "..#..#..#..#..#",
                  "###..#####..###",
                  "###..####..####",
                  "#.##.####..#..#",
                  "####..###..####",
                  "####..####.####",
                  "####.##.#..#..#",
                  "####.#####.####",
                  "####.####..####",
                  "....#.###.#...."};

signed main() {
//    freopen("in", "r", stdin), freopen("out", "w", stdout);
    mp["####.##.##.####"] = 0;
    mp["..#..#..#..#..#"] = 1;
    mp["###..#####..###"] = 2;
    mp["###..####..####"] = 3;
    mp["#.##.####..#..#"] = 4;
    mp["####..###..####"] = 5;
    mp["####..####.####"] = 6;
    mp["####.##.#..#..#"] = 7;
    mp["####.#####.####"] = 8;
    mp["####.####..####"] = 9;
    mp["....#.###.#...."] = 10;
    int Test;
    cin >> Test;
    for (int Case = 1; Case <= Test; Case++) {
        string s;
        getline(cin, s);
        for (auto &i : a) {
            getline(cin, i);
            n = i.size();
        }
        int ans = 0;
        int sum = 0;
        for (int j = 0; j < n; j += 4) {
            string sub = "";
            for (int i = 0; i < 5; i++)sub += a[i].substr(j, 3);
            int tmp = mp[sub];
            if (tmp == 10)ans += sum, sum = 0;
            else sum = sum * 10 + tmp;
        }
        ans += sum;
        string res[5] = {"", "", "", "", ""};
        if (ans == 0) {
            for (int i = 0, j = 0; i < 5; i++, j += 3) {
                res[i] = num[0].substr(j, 3) + res[i];
            }
        } else {
            while (ans) {
                for (int i = 0, j = 0; i < 5; i++, j += 3) {
                    res[i] = num[ans % 10].substr(j, 3) + res[i];
                }
                ans /= 10;
                if (ans) {
                    for (int i = 0; i < 5; i++)res[i] = "." + res[i];
                }
            }
        }
        for (int i = 0; i < 5; i++)cout << res[i] << endl;
        if (Case < Test)cout << endl;
    }
    return 0;
}

图像存储

先求出联通块的数量,然后再对联通块判重即可

#include <bits/stdc++.h>

using namespace std;
int mp[300][300], mv[4][2] = {{1,  0},
                              {-1, 0},
                              {0,  1},
                              {0,  -1}};
int n, m, cnt;

set<vector<pair<int, int> > > S1;

void Bfs(int x, int y) {
    queue<pair<int, int> > Q;
    vector<pair<int, int> > v;
    Q.push({x, y});
    v.push_back({x, y});
    int mx = x, my = y;
    mp[x][y] = 0;
    while (Q.size()) {
        pair<int, int> top = Q.front();
        Q.pop();
        for (int i = 0; i < 4; i++) {
            int nx = top.first + mv[i][0];
            int ny = top.second + mv[i][1];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny]) {
                mp[nx][ny] = 0;
                Q.push({nx, ny});
                v.push_back({nx, ny});
                mx = min(mx, nx), my = min(my, ny);
            }
        }
    }
    for (int i = 0; i < v.size(); i++) {
        v[i].first -= x;
        v[i].second -= y;
    }
    S1.insert(v);
}

int main() {
    //freopen("in", "r", stdin);
    while (cin >> n >> m) {
        if (!n && !m) break;
        cnt = 0, S1.clear();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                scanf("%1d", &mp[i][j]);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (mp[i][j]) cnt++, Bfs(i, j);
            }
        }
        cout << cnt << ' ' << S1.size() << endl;
    }
    return 0;
}

坐标计数

所有的坐标都会在有限次数内变成(0,0),所以只要统计区域内坐标个数即可。

#include <bits/stdc++.h>

#define ll long long
using namespace std;

int t;
ll x_1, y_1;
ll x_2, y_2;

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%lld%lld%lld%lld", &x_1, &y_1, &x_2, &y_2);
        printf("%lld\n", (x_2 - x_1 + 1) * (y_2 - y_1 + 1));
    }
    return 0;
}

解方程

答案即为求的因子个数

#include <bits/stdc++.h>

#define MaxN 1000000
using namespace std;
bool m[MaxN];
long long p[78500];
int ps;

void genp() {
    int i, j, k, q = int(sqrt(MaxN));
    p[0] = 2;
    for (i = 3; i < q; i += 2) {
        if (!m[i]) for (k = 2 * i, j = i * i; j < MaxN; j += k) m[j] = true;
    }
    for (ps = 1, i = 3; i < MaxN; i += 2)
        if (!m[i]) p[ps++] = i;
}

int main() {
    genp();
    long long k1, k2, kk;
    int cnt, i, t, ans;
    cin >> t;
    while (t--) {
        cin >> k1 >> k2;
        kk = k1 * k2;
        if (kk == 1) cout << 1 << endl;
        else {
            ans = 1;
            for (i = 0; i < ps && kk != 1; ++i) {
                for (cnt = 1; kk % p[i] == 0; ++cnt) kk /= p[i];
                ans *= cnt;
            }
            if (kk != 1) ans *= 2;
            cout << ans << endl;
        }
    }
    return 0;
}

消减整数

直接模拟会超时。

假设第一次不够减时,是想要减K而只剩下M。则第二次就会剩下,第三次剩下,直到剩下的数量大于等于K时减去K,减去K后剩下的又不足K,又要重头开始减。

所以题目就转化为:每次加M,大于等于K时就减掉K,问多少次以后归零。

稍加观察可以发现,归零的时候加的数值总和为K的倍数,而能够加的有只有M的倍数,所以题目是在问:最快加了多少次以后变为M,K的公倍数,就是求最小公倍数。

#include <bits/stdc++.h>

#define ll long long
using namespace std;

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int cal(int n) {
    int l = 1, r = n, ans = l;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if ((1 + mid) * mid / 2 <= n) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    return ans;
}

int main() {
//    freopen("in", "r", stdin);
    int t, n;
    cin >> t;
    while (t--) {
        scanf("%d", &n);
        int k = cal(n);
        int m = n - (1LL + k) * k / 2;
//        cout << n << ' ' << k << ' ' << m << ' ';
        if (m == 0) puts("1");
        else printf("%d\n", (k+1) / gcd(m, (k+1)));
    }
    return 0;
}

简单题的逆袭

注意long long overflow,大多数人都WA在这里了

考虑x=0

考虑x=1

考虑y=0

#include <iostream>

using namespace std;

typedef long long ll;

ll cal(ll ta, ll tb) {
    if (ta <= 1 || tb == 0) return -1;
    ll ans = 0;
    while (tb >= ta) ans++, tb /= ta;
    return ans;
}

int main() {
    long long int a, n;
    int t;
    cin >> t;
    while (t--) {
        scanf("%lld%lld", &a, &n);
        cout << cal(a, n) << endl;
    }

    return 0;
}

对称之美

判断第一个和最后一个有无相同的字母

判断第二个和倒数第二个有无相同的字母

。。。

#include <bits/stdc++.h>

using namespace std;

int n, t;
string s[100];

bool match(string s1, string s2) {
    bool used[26];
    for (int i = 0; i < 26; ++i)
        used[i] = false;
    for (int i = s1.size() - 1; i >= 0; --i)
        used[s1[i] - 'a'] = true;
    for (int i = s2.size() - 1; i >= 0; --i)
        if (used[s2[i] - 'a']) return true;
    return false;
}

int main() {

    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 0; i < n; ++i) cin >> s[i];
        bool yes = true;
        for (int i = 0; i < n / 2; ++i) {
            if (!match(s[i], s[n - 1 - i])) {
                yes = false;
                break;
            }
        }
        if (yes) puts("Yes");
        else puts("No");
    }
    return 0;
}

非对称之美

只有n,n-1,0三种情况

分别判一下即可

#include<bits/stdc++.h>

//#define int long long
const int Maxn = 1000000005;
using namespace std;

signed main() {
    //freopen("in","r",stdin);
    string s;
    cin >> s;
    if (s.size() == 1) {
        cout << "0" << endl;
        return 0;
    }
    if (s.size() == 2) {
        if (s[0] == s[1]) {
            cout << "0" << endl;
        } else {
            cout << 2 << endl;
        }
        return 0;
    }
    int i;
    int flag1 = 0;
    int flag2 = 0;
    for (i = 0; i < s.length() / 2; i++) {
        if (s[i] != s[s.length() - 1 - i]) {
            flag1 = 1;
            break;
        }
    }
    if (flag1 == 1) {
        cout << s.length() << endl;
        return 0;
    }
    int l = s.length() - 1;
    for (i = 0; i < l / 2; i++) {
        if (s[i] != s[l - 1 - i]) {
            flag2 = 1;
            break;
        }
    }
    if (flag2 == 1) {
        cout << s.length() - 1 << endl;
        return 0;
    }
    cout << "0" << endl;
    return 0;
}

排列算式

这个题900次提交里有800次左右都是一个人交的!

贪心(贪婪)(greedy)算法。

分以下三步:

1、就是先考虑(-3),依次判断能否让(+3)或2×(+2)+(-1)或(+2)+(+1)或3×(+1)把它中和掉(加起来=0)(注意判断顺序!!!),直到没有(-3)为止;若在中途没有东西能够中和(-3),则“NO”。

2、然后考虑(+3),当(+3)的个数大于1时(想想为什么),依次判断能否让(-3)或2×(-2)+(+1)或(-2)+(-1)或3×(-1)把它中和(注意判断顺序!!!),直到(+3)个数小于等于1为止;若在中途没有东西能够中和(+3),则“NO”。

3、若都没有“NO”,则“YES”。

#include <stdio.h>
#include <string.h>

int *num, a[7];

int min(int a, int b) { return (a > b) ? b : a; }

int check() {
    int t = 0;
    for (int i = -3; i <= 3; i++)
        t += num[i] * i;
    if (t > 3 || t < 0)
        return 0;

    t = min(num[3], num[-3]);
    num[3] -= t;
    num[-3] -= t;

    t = min(num[2], num[-1]);
    num[2] -= t;
    num[-1] -= t;
    num[1] += t;

    t = min(num[-3], num[1]);
    num[1] -= t;
    num[-3] -= t;
    num[-2] += t;

    if (num[-3])
        return 0;


    t = min(num[-2], num[1]);
    num[-2] -= t;
    num[1] -= t;
    num[-1] += t;

    t = min(num[3], num[-1]);
    num[-1] -= t;
    num[3] -= t;
    num[2] += t;
    if (num[3] > 1)
        return 0;
    return 1;
}

int main() {
    int n, t, now;
    num = &a[3];
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        memset(a, 0, sizeof(a));
        while (n--) {
            scanf("%d", &now);
            num[now]++;
        }
        printf("%s\n", check() ? "YES" : "NO");
    }
    return 0;
}

全部评论

(4) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐