在学考复习的时候偷偷打了这场cf,今天才有时间把题目都订正,囧
B
题目大意:n个人,每个人向一些人发邮件(都会给自己发),然后构造一个确定某些人发邮件的方案,使得每个人收到的邮件 $ eq a[i]$
题解:因为每个人都会给自己发,所以当某人$= a[i]$时就把他自己选上
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 #define rep(i, l, r) for (int i = l; i <= r; i++) 8 #define drep(i, r, l) for (int i = r; i >= l; i--) 9 typedef long long ll; 10 const int N = 108; 11 int n, a[N], d[N], ans[N]; 12 char Map[N][N]; 13 int main() 14 { 15 #ifndef ONLINE_JUDGE 16 freopen("input.txt","r",stdin); 17 //freopen("output.txt","w",stdout); 18 #endif 19 scanf("%d", &n); 20 rep(i, 1, n) scanf("%s", Map[i] + 1); 21 rep(i, 1, n) scanf("%d", &a[i]); 22 bool flag = false; 23 while (!flag) 24 { 25 flag = 1; 26 rep(i, 1, n) if (a[i] == d[i]) 27 { 28 flag = 0; 29 ans[++ans[0]] = i; 30 rep(j, 1, n) if (Map[i][j] == '1') d[j]++; 31 break; 32 } 33 } 34 sort(ans + 1, ans + ans[0] + 1); 35 printf("%d ", ans[0]); 36 rep(i, 1, ans[0]) printf("%d ", ans[i]); printf(" "); 37 #ifndef ONLINE_JUDGE 38 fclose(stdin); fclose(stdout); 39 #endif 40 return 0; 41 }
C
题目大意:S和D玩游戏,S先手。一共n堆石头,每次可以拿走一堆,剩下k堆就结束。S希望最后留下的石头总数是奇数,D希望是偶数
题解:特判n = k的情况,然后注意到最后一次操作时,如果奇偶石头都有,那么操作的人就获胜了。根据这个来分类讨论
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 #define rep(i, l, r) for (int i = l; i <= r; i++) 8 #define drep(i, r, l) for (int i = r; i >= l; i--) 9 typedef long long ll; 10 const int N = 2e5 + 8; 11 int n, k, a[N], c[2]; 12 void out(int x) 13 //x:最后的奇偶性 14 { 15 printf("%s ", x == 1 ? "Stannis " : "Daenerys"); 16 } 17 void solve() 18 //先手奇数,后手偶数 19 { 20 if (!c[1]) {out(0); return;} 21 if (!c[0]) {out(k & 1); return;} 22 if ((n - k) & 1) 23 //先手控制最后一步 24 { 25 int x = (n - k) / 2 + 1, y = n - k - x; x--; 26 if (y >= c[1]) out(0); 27 else if (y < c[0]) out(1); 28 else out(k & 1); 29 } 30 else 31 //后手控制最后一步 32 { 33 int x = (n - k) / 2, y = n - k - x; y--; 34 if (x >= c[0]) out(k & 1); 35 else out(0); 36 } 37 } 38 int main() 39 { 40 #ifndef ONLINE_JUDGE 41 freopen("input.txt", "r", stdin); 42 //freopen("output.txt", "w", stdout); 43 #endif 44 scanf("%d%d", &n, &k); 45 rep(i, 1, n) scanf("%d", &a[i]), c[(a[i] & 1) ? 1 : 0]++; 46 if (n == k) 47 { 48 out(c[1] & 1); 49 return 0; 50 } 51 solve(); 52 #ifndef ONLINE_JUDGE 53 fclose(stdin); fclose(stdout); 54 #endif 55 return 0; 56 }
D
题目大意:给一个$n imes m$的矩阵,矩阵有黑白格子,选择最少的前缀矩阵,使得可以计算所有黑格子上的权值和-白格子权值和
题解:倒着扫一遍,贪心选择,使得白格子是-1,黑格子是1
E
不会
F
题目大意: 给定一个序列,求有多少个长度大于等于2的区间满足区间和$-$区间最大值是$k$的倍数
题解: 递归解决。假设现在处理区间$[l, r]$,先找到最大值,然后只遍历短的那一边,计算当前的和,然后算出另一边应该是多少,则问题变成了求一段区间等于某个值的有多少个,可以用主席树解决
时间复杂度$O(n{log^2}n)$
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; #define rep(i, l, r) for (int i = l; i <= r; i++) #define drep(i, r, l) for (int i = r; i >= l; i--) typedef long long ll; const int N = 3e5 + 8, Log = 23; int n, k, tot, a[N], sum[N], st[Log][N], son[N], rt[N]; ll ans; struct Node { int l, r, s; }t[N * Log]; void Insert(int x, int o, int l, int r, int p, int d) { t[x].s = t[o].s + d; if (l == r) return; int mid = l + r >> 1; if (p <= mid) t[x].r = t[o].r, Insert(t[x].l = ++tot, t[o].l, l, mid, p, d); else t[x].l = t[o].l, Insert(t[x].r = ++tot, t[o].r, mid + 1, r, p, d); } int query(int x, int o, int l, int r, int p) { if (l == r) return t[x].s - t[o].s; int mid = l + r >> 1; if (p <= mid) return query(t[x].l, t[o].l, l, mid, p); return query(t[x].r, t[o].r, mid + 1, r, p); } void init() { rep(i, 1, n) { son[i] = son[i - 1]; if ((1 << son[i] + 1) == i) son[i]++; } rep(i, 1, n) st[0][i] = i; rep(i, 1, son[n]) rep(j, 1, n) if (j + (1 << i) - 1 <= n) { int x = st[i - 1][j], y = st[i - 1][j + (1 << i - 1)]; st[i][j] = a[x] > a[y] ? x : y; } rep(i, 0, n) { if (i) sum[i] = (sum[i - 1] + a[i]) % k; Insert(rt[i + 1] = ++tot, rt[i], 0, k - 1, sum[i], 1); } } int stquery(int l, int r) { int k = son[r - l + 1]; int x = st[k][l], y = st[k][r - (1 << k) + 1]; return a[x] > a[y] ? x : y; } void solve(int l, int r) { if (l == r) {ans++; return;} if (l > r) return; int p = stquery(l, r); //printf("_______%d %d %d ", l, r, p); ll tmp = ans; if (p - l + 1 <= r - p + 1) { int s = 0; drep(i, p, l) { if (i != p) s = (s + a[i]) % k; int x = (-s + k + sum[p]) % k; ans += query(rt[r + 1], rt[p], 0, k - 1, x); } } else { int s = 0; rep(i, p, r) { if (i != p) s = (s + a[i]) % k; int x = (sum[p - 1] + k - (-s + k)) % k; //printf("%d %d %d ", x, p - 1, max(l - 2, 0)); ans += query(rt[p], rt[l - 1], 0, k - 1, x); } } //printf("%I64d ", ans - tmp); solve(l, p - 1); solve(p + 1, r); } int main() { #ifndef ONLINE_JUDGE freopen("input.txt","r",stdin); //freopen("output.txt","w",stdout); #endif scanf("%d%d", &n, &k); rep(i, 1, n) scanf("%d", &a[i]); init(); solve(1, n); ans -= n; printf("%I64d ", ans); #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); #endif return 0; }
G
题目大意:给一个序列,每次可以交换$a[i]$和$a[i + 1]$,但是交换前$a[i]$要给一块钱给$a[i + 1]$,如果$a[i]$为$0$就不能交换,求最后的序列,使得序列不降
题解: 如果把n个数放在楼梯上(也就是$a[i] + i$),那么交换两个数就相当于把楼梯这两级包括上面的一起交换。显然要把高的往后面放。所以直接按照$a[i] + i$排序,然后再复原,检验答案
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 #define rep(i, l, r) for (int i = l; i <= r; i++) 8 #define drep(i, r, l) for (int i = r; i >= l; i--) 9 typedef long long ll; 10 const int N = 200008; 11 int n, a[N]; 12 int main() 13 { 14 #ifndef ONLINE_JUDGE 15 freopen("input.txt","r",stdin); 16 //freopen("output.txt","w",stdout); 17 #endif 18 scanf("%d", &n); 19 rep(i, 1, n) scanf("%d", &a[i]), a[i] += i; 20 sort(a + 1, a + n + 1); 21 rep(i, 1, n) a[i] -= i; 22 rep(i, 1, n - 1) if (a[i] > a[i + 1]) 23 { 24 printf(":( "); return 0; 25 } 26 rep(i, 1, n) printf("%d ", a[i]); printf(" "); 27 #ifndef ONLINE_JUDGE 28 fclose(stdin); fclose(stdout); 29 #endif 30 return 0; 31 }
H
题目大意:给出矩阵$A$,构造权值为0矩阵$B$,使得$A - B$每一项绝对值的最大值最小
题解:二分答案,然后A的四个值变成了四个范围,然后求出$ac$和$bd$的范围,判断是否相交。注意因为有负数所以最小值乘最小值不一定就是最小值
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 #define rep(i, l, r) for (int i = l; i <= r; i++) 8 #define drep(i, r, l) for (int i = r; i >= l; i--) 9 typedef long long ll; 10 typedef double real; 11 #define double long double 12 const double eps = 1e-10; 13 int a, b, c, d; 14 double ans; 15 double getmin(double x, double y, double z) 16 { 17 double v1 = (x - z) * (y - z); 18 double v2 = (x + z) * (y - z); 19 double v3 = (x + z) * (y + z); 20 double v4 = (x - z) * (y + z); 21 return min(v1, min(v2, min(v3, v4))); 22 } 23 double getmax(double x, double y, double z) 24 { 25 double v1 = (x - z) * (y - z); 26 double v2 = (x + z) * (y - z); 27 double v3 = (x + z) * (y + z); 28 double v4 = (x - z) * (y + z); 29 return max(v1, max(v2, max(v3, v4))); 30 } 31 int main() 32 { 33 #ifndef ONLINE_JUDGE 34 freopen("input.txt","r",stdin); 35 //freopen("output.txt","w",stdout); 36 #endif 37 scanf("%d%d", &a, &b); 38 scanf("%d%d", &c, &d); 39 double l = 0, r = 1e9; 40 while (fabs(r - l) > eps) 41 { 42 double mid = (l + r) / 2.0; 43 double l1 = getmin(a, d, mid); 44 double r1 = getmax(a, d, mid); 45 double l2 = getmin(b, c, mid); 46 double r2 = getmax(b, c, mid); 47 if (r2 < l1 || l2 > r1) l = mid; 48 else r = mid; 49 } 50 printf("%.10lf ", (real)l); 51 #ifndef ONLINE_JUDGE 52 fclose(stdin); fclose(stdout); 53 #endif 54 return 0; 55 }