AC情况
第一题 | 第二题 | 第三题 | 第四题 |
---|---|---|---|
AC 100 | TLE 0 (AC 100) | - (AC 100) | - |
赛中概况
第一题很快AC,后面第二题超时,剩下的题目没有看。
解题报告
异或(xor)
情况:
AC
题意:
给定 n n n 个二进制数,求 a 1 ⊕ a 2 ⊕ . . . a n a_1 \oplus a_2 \oplus ... a_n a1⊕a2⊕...an 的结果。
题解:
模拟即可。
int get(char c) {
return c - '0';
}
for (int i = 1; i <= n; i++) {
cin >> s;
if (i == 1) ans = s;
else {
for (int i = 0; i < m; i++)
ans[i] = get(ans[i]) ^ get(s[i]) + '0';
}
}
数学游戏(math)
情况:
TLE (AC)
题意:
f ( i ) f(i) f(i): i i i 各位数字相加的和。
求出 x x x 的所有约数 d d d.
g ( x ) = ∑ ( x d i × f ( d i ) ) g(x) = \sum (x^{d_i} \times f(d_i)) g(x)=∑(xdi×f(di))
求 ∑ i = 1 n g ( i ) \sum^n_{i=1}g(i) ∑i=1ng(i)(答案对 1 0 9 + 7 10^9 + 7 109+7 取余).
赛事做法:
模拟求解。
题解:
先把从 1 1 1 到 n n n 所有的 f ( i ) f(i) f(i) 都求出来。
运用类似于埃氏筛的方法:
遍历 i ( 1 ≤ i ≤ n ) i (1 \le i \le n) i(1≤i≤n),再遍历 j ( 1 ≤ j ≤ ⌊ n i ⌋ ) j (1 \le j \le \lfloor\frac{n}{i}\rfloor) j(1≤j≤⌊in⌋) ,找出 i i i 在 [ 1 , n ] [1,n] [1,n] 范围内所有的倍数,那么 i i i 一定是 g ( i j ) g(ij) g(ij) 的一个约数。 g ( i j ) g(ij) g(ij) 累计 f ( i ) × ( i j ) i f(i) \times (ij)^i f(i)×(ij)i.
取余的性质(取余的性质不适用于除法):
( a ± b ) % c = ( a % c ± b % c ) % c , a × b % c = [ ( a % c ) × ( b % c ) ] % c (a \pm b) \% c = (a \% c \pm b \% c) \% c, \ \ a \times b \% c =[(a \% c) \times (b \% c)] \% c (a±b)%c=(a%c±b%c)%c, a×b%c=[(a%c)×(b%c)]%c
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const long long int mod = 1e9 + 7;
const long long int N = 1000010;
ll n, p[N], g[N];
ll f(ll n) {
ll res = 0;
while (n) {
res += n % 10;
n /= 10;
}
return res % mod;
}
ll qpow(ll a, ll b, ll mod) {
a %= mod;
ll res = 1;
while (b > 0) {
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
int main() {
cin >> n;
for (ll i = 1; i <= n; i++)
p[i] = f(i);
ll res = 0;
for (ll i = 1; i <= n; i++)
for (ll j = 1; i * j <= n; j++)
g[i * j] = (g[i * j] + p[i] * qpow(i * j, i, mod); % mod) % mod;
for (ll i = 1; i <= n; i++)
res = (res + g[i]) % mod;
cout << res;
return 0;
}
交换(swap)
情况:
−
-
− (AC)
题意:
有一个长度为 n n n 的序列 a 1 , a 2 , . . . a n a_1,a_2,...a_n a1,a2,...an ,有 k k k 次操作机会,每次操作可选择任何一个数字(可以重复选择同一个数字)的任意两位进行交换(但不能出现交换后有前导 0 0 0 的情况),问进行 k k k 次操作后,序列内最小的 a i a_i ai 最大是多少。
题解:
二分查找临界值,保证所有数一共进行 k k k 次操作后都可以达到临界值及以上。
change()
函数:对数字
n
n
n 进行一次操作。策略:从最高位到最低位寻找第一个可以被替换的位(可以被替换的位是指,这一位的后面还有比它更大的数字),把它改成后面最大的数字,然后替换掉后面的那个大数字的位(有相同数字的位替换位较低的)。代码如下
int change(int n) {
priority_queue<int> q;
queue<int> Q;
int m = n, a[15], cnt = 0;
while (m) {
q.push(m % 10);
a[++cnt] = m % 10;
m /= 10;
}
int res = 0, flag = -1, ch;
for (int i = cnt; i >= 1; i--) {
if (a[i] == q.top()) q.pop();
if (a[i] < q.top() && flag == -1) {
ch = a[i];
a[i] = q.top();
flag = q.top();
}
else if (flag != -1 && a[i] == flag && a[i - 1] != a[i]) {
a[i] = ch;
break;
}
}
for (int i = cnt; i >= 1; i--)
res = res * 10 + a[i];
return res;
}
check()
函数:判断临界值
m
i
d
mid
mid 是否满足情况,即所有的数一共进行
k
k
k 次操作。策略:如果这个数小于临界值并且还有操作次数,那么一直对这个数进行操作,直到操作次数没有,或者已经在临界值及以上,或者已经把这个数操作到最大无法继续替换了(也就是每位上的数字成降序排列),不再进行操作。一旦遇到一个已经无法继续进行操作而且仍然小于临界值的数字,视为该临界值过高,无法满足情况。如果所有数字经合法的操作之后都能达到临界值,则视为该临界值为一个符合要求的解。代码如下
bool check(int mid) {
int had = k;
for (int i = 1; i <= n; i++) b[i] = a[i];
for (int i = 1; i <= n; i++) {
while (b[i] < mid && b[i] != change(b[i]) && had > 0) {
b[i] = change(b[i]);
had--;
}
if (b[i] < mid) return false;
}
return true;
}
main()
主函数:二分查找最大的合法临界值。临界值的上限就是能够输入的最大值
1
0
9
10^9
109,最小值是能够输入的最小值
1
1
1。
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
int l = 1, r = 1e9 + 10, mid, ans = 0;
while (l <= r) {
mid = (l + r) / 2;
if (check(mid)) {
ans = max(ans, mid);
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans;
return 0;
}
数字串(string)
情况:
− - −
题意:
现有一个长度为 n n n 的不含有 0 0 0 的数字字符串,可以进行以下操作:
- 选择若干个不相交的连续子段并进行翻转,翻转一个长度为 L L L 的子段代价为 L L L
- 在字符串中插入不超过 n − 1 n-1 n−1 个乘号,代价为0
现在最多可使用 v v v 的代价,问进行多次操作之后,字符串计算结果对 m m m 取余后最大是多少。
总结
多想一想以前学过的算法。