题解 | #ABC#
活着的证据
https://ac.nowcoder.com/acm/contest/11178/A
A.活着的证据
- 在保位数不超过n的前提下,尽可能地增大数的位数。
- 从左往右填,先尽可能地填5,,若没填满n位且还有1剩余,在用剩下的1填后面的位。
- 填完1轮后,若还有1剩余,把剩下的1用完或把每个数加到8为止。
- 注意加的时候对5来说能加3,对1来说只能加1
#include <bits/stdc++.h> #define x first #define y second using namespace std; typedef long long LL; typedef pair<int, int> PII; const int N = 5000010; int w[N], n, m; int u, v; int main(){ int T; scanf("%d", &T); while (T--) { scanf("%d%d%d", &u, &v, &n); for (int i = 1; i <= n; i ++) w[i] = 0; if (u + v <= n) { //V+I的个数<=n while (u --) printf("5");//前面全填5 while (v --) printf("1");//后面全填3 } else { //V+I的个数>n int i; //先贪心地填满n位 for (i = 1; i <= n && u; u --, i ++) w[i] = 5; //先把5填完 for (; i <= n && v; v --, i ++) w[i] = 1; //填2 //把剩下的1用完 for (i = 1; i <= n && v; i ++) { int t; if (w[i] == 5) t = min(3, v);//填5,最多能加3 else t = min(2, v); //填1,最多能加2 v -= t; w[i] += t; } for (i = 1; i <= n; i ++) printf("%d", w[i]); } puts(""); } return 0; }
B.寻寻觅觅寻不到
定义:
1. l[i]:表示s与t的前i个字符是否匹配
2. r[i]:表示s与t从后往前错位匹配是否可以匹配到i
l[i]好理解,那么r[i]的错位匹配是什么意思呢。
假设输入的字符串分别是s和t(s,t的长度为n),要截取的长度是k。因为t是s截取一段长度为k的字串搬到后面得到的,t原来的末尾位置是从n-k。从后往前匹配的时候,s从n开始,t从n-k开始枚举。
举个栗子: s="abcdef" t="abefcd" k=2 l数组为:110000 r数组为:000011 (s从n开始枚举,t从n-2开始枚举)
预处理出两个数组后,枚举截取的起点。当s中去掉截取的字串后的前后缀与t的前后缀匹配,并且t的后k为与s截取的部分匹配的时候,就说明截取这个区间放到后面可以得到t。
#include <bits/stdc++.h> #define x first #define y second using namespace std; typedef long long LL; typedef pair<int, int> PII; const int N = 300010; char s[N], t[N]; int n, m, k; bool l[N], r[N]; //l[i]=1表示s与t的前i个字符匹配 //r[i]=1表示s与t从后往前错位匹配可以匹配到i bool check(int i, int j) { //判断s截取的部分是否与t的后k个字符匹配 for (int u = 1; u <= k; u ++, i ++, j ++) { if (s[i] != t[j]) return false; } return true; } int main() { int T; cin >> T; while (T --) { cin >> s + 1 >> t + 1 >> k; n = strlen(s + 1), m = strlen(t + 1); if (n != m) { cout << "NO\n"; continue; } for (int i = 0; i <= n; i ++) l[i] = r[i] = 0; //初始化 l[0] = r[n + 1] = 1; //注意边界:在截取的区间是s[n-k,n]会用到 for (int i = 1; i <= n; i ++) l[i] = l[i - 1] && (s[i] == t[i]); //前缀匹配 for (int i = n, j = m - k; i >= 1 && j >= 1; i --, j --) r[i] = r[i + 1] && (s[i] == t[j]);//后缀错位匹配 bool flag = 0; for (int i = 1; i <= n - k + 1; i ++) { //遍历截取的起点 if (l[i - 1] && r[i + k] && check(i, m - k + 1)) { flag = 1; break; } } if (flag) cout << "YES\n"; else cout << "NO\n"; } return 0; }
C.踩不出足迹
根据题目给出的提示可以发现,同或就是对异或取反得到的结果。我们先把n-1次操作全看成是异或操作假设n-1次异或操作后得到的答案是t。无论中间步骤进行了多少次取反操作,最终答案我们都只会得到两种值t和t取反后的值,因此我们只要比较两种值的大小就行。
注意题目的输入刚好卡到了2的64次方,要用unsigned long long。另外如果像下面代码的这种写法,要对特判,因为1<<64也会爆unsinged long long
#include <bits/stdc++.h> #define x first #define y second using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> PII; int main() { int n, k; LL ans = 0; cin >> n >> k; while (n --) { ULL x; cin >> x; ans ^= x; } if (n == 1) cout << ans; else { LL tem = ~ans, tem2 = 0; if (k < 64) tem2 |= tem & ((1ull << k) - 1); else tem2 = tem; cout << max(ans, tem2); } return 0; }