复健训练-CF1698(Codeforces Round #803 (Div. 2))
【写完签到题就不会了...
A. XOR Mixup
题意:给一个 n 个数的序列 ${a_i}$ ,其中一个数是剩下的数的异或和,让你找出这个数。
做法:直接枚举。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 105; int n, a[N]; void Main(){ scanf("%d", &n); int XOR = 0; rep (i,1,n) scanf("%d", &a[i]), XOR ^= a[i]; rep (i,1,n) if ((XOR^a[i]) == a[i]){printf("%d\n", a[i]); return;} return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
B. Rising Sand
题意:n 堆沙子每堆高度为 $a_i$ ,定义一堆沙子 too tall 为,$1<i<n$ 且 $a_i > a_{i-1} + a_{i+1}$ 。现在一个操作可以将一段长度为 k 的沙子的 $a_i$ 都加 1,问经过若干操作,可以最多使得多少沙子为 too tall 的。
做法:如果 $k \ge 2$ ,那么给 $a_i + 1$ 的同时势必会给 $a_{i-1}$ 或 $a_{i+1}$ 加上 1 ,那么没法使得 $a_i - a_{i-1} - a{i+1}$ 的值变得更大,所以 too tall 数量就是原来的 too tall 数量。而 $k = 1$ 时一定可以取到最大值 $\frac{n-1}{2}$ 。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 2e5+10; int n, k, a[N]; void Main(){ scanf("%d%d", &n,&k); rep (i,1,n) scanf("%d", &a[i]); if (k==1){ printf("%d\n", (n-1)/2); } else{ int ans = 0; rep (i,2,n-1) if ((ll)a[i] > (ll)a[i-1]+a[i+1]) ans++; printf("%d\n", ans); } return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
C. 3SUM Closure
题意:定义一个序列是 3SUM-closed 当且仅当对于序列中任意一个三元组的和都在序列中出现过。给一个序列问是否是 3SUM-closed 的。
做法:首先可以发现序列中如果有 $\ge 3$ 个正数或负数必定是 NO ,因为必定可以找到最大 / 小的三个正数 / 负数相加在原数列中没有出现过。而任意多的 0 可以等价为一个 0 。所以原数列转化之后最多有 5 个数。直接暴力判断一下即可。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 2e5+10; int n, a[N], b[10]; void Main(){ scanf("%d", &n); int x = 0, y = 0, z = 0; rep (i,1,n){ scanf("%d", &a[i]); if (a[i]>0) x ++; else if (a[i]<0) y ++; else z ++; } if (x>=3 || y >= 3){puts("NO"); return;} int tot = 0; map<int,bool> mp; rep (i,1,n) if (a[i]!=0) b[++tot] = a[i],mp[a[i]]=1; if (z) b[++tot] = 0,mp[0]=1; rep (i,1,tot) rep (j,i+1, tot) rep (k,j+1, tot) if (!mp.count(b[i]+b[j]+b[k])){puts("NO"); return;} puts("YES"); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
D. Fixed Point Guessing
题意:交互题,有一个 ${a} = [1..n]$ 的排列,n 为奇数,现在执行 $\frac{n-1}{2}$ 次交换,每次交换的数互不相同,交换完之后还有一个数的位置没有改变。你现在可以每次询问一个 $?\ l\ r$ ,回答会给出 $[l, r]$ 中的数从小到大排序的序列。 $n\le 10^4$ ,最多询问 15 次,要找到那个位置没有变的数。
做法:看询问次数感觉大概是 $log$ 级别的,就往二分的方向上去想。然后再思考一个区间给出了里面的数以后,我们可以知道哪些数是在 $[l,r]$ 范围内,哪些数不在。如果不在肯定是从外面交换进来的,如果在的话肯定是两两交换,而如果该区间内有我们要找的数,那么它最后是成单的,所以在 $[l,r]$ 范围内的数一定是奇数个。所以我们二分查找每次看左右两个区间哪个是在 $[l,r]$ 范围内的数是奇数个就往哪里走。不断缩小范围,最后找到那个数。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; int ask(int l,int r){ printf("? %d %d\n", l, r); fflush(stdout); int t = 0; rep (i,1,r-l+1){ int x; scanf("%d", &x); if (l<=x && x<=r) t ++; } return t; } void answer(int x){ printf("! %d\n" , x); fflush(stdout); } void Main(){ int n; scanf("%d", &n); int l = 1, r = n, mid; while (l < r){ mid = (l+r)>>1; if (ask(l, mid)%2==1) r = mid; else l = mid+1; } answer(l); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
E. PermutationForces II
题意:给一个 [1..n] 的排列 a 和一个 s ,现在可以进行 n 次操作,第 i 次可以选择 $i\le x\le y \le \min(i+s, n)$ 的 x y,将 ${a}$ 中 x 和 y 交换(注意不是 $a_x$ $a_y$ 交换,是值交换,以及 x 可以等于 y)。现在给出一个排列 b ,有些位置有空缺 -1 ,问有多少补全 b 的方法,使得 a 可以经过一系列操作变成 b 。
做法:【是看这个博客的题解的,写的真的很好,就是我自己想不出来..】
首先是考虑判定,假设有一个已知的 b ,要判断 a 是否能变成 b ,那么肯定是从 1 开始,看 1 最终位置在哪里,看在 a 中能否交换变到 b 中的位置。然后再看 2 , 3 .... 这样可以发现每步操作都是唯一的,而且进一步可以发现,假如把 a 按照 b 从小到大排序,相当于要把 a 变成 [1..n] 的排列,第 i 个数能否操作成功就是看 $a_i$ 是否 $\le i + s$ 。那么这里的 i 就是 $b_i$ ,所以回到原序列约束条件就是 $a_i \le b_i + s$ ,即 $b_i \ge a_i - s$ 。那么无解的情况就好判了。有解的话对于所有 -1 位置,可以算出 $b_i$ 的一个范围,根据这个范围算出答案数量,这就是个组合数学乘法原理的问题了,具体可以见代码。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 2e5+10; const int mod = 998244353; int n, s, a[N], b[N], c[N], m, sum[N]; void Main(){ scanf("%d%d", &n, &s); rep (i,1,n) scanf("%d", &a[i]), sum[i] = 0; bool flag=1; rep (i,1,n){ scanf("%d", &b[i]); if (b[i]!=-1) sum[b[i]]++; if (b[i]!=-1&&b[i]<a[i]-s) flag=0; } for (int i=n-1; i>=1; i--) sum[i]+=sum[i+1]; if (!flag){puts("0"); return;} m = 0; rep (i,1,n) if (b[i]==-1) c[++m] = max(a[i]-s, 1); sort(c+1, c+1+m); reverse(c+1, c+1+m); int ans = 1; rep (i,1,m) ans = (ll)ans*max(n-c[i]+1-sum[c[i]]-i+1, 0)%mod; printf("%d\n", ans); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
F G 咕咕 还没看题(