【题解】牛客小白月赛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;
}
阿里云成长空间 747人发布
查看1道真题和解析