Codeforces Round #707 Div2 A-D
A - Alexey and Train
模拟,没啥需要讲的,按照题目写就行了。
#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 309; int n, a[N], b[N], tim[N]; void work() { n = read(); for(int i = 1; i <= n; i++) { a[i] = read(); b[i] = read(); } for(int i = 1; i <= n; i++) tim[i] = read(); int now = a[1] + tim[1]; for(int i = 2; i <= n; i++) { if(now + (b[i - 1] - a[i - 1] + 1) / 2 >= b[i - 1]) { now = now + (b[i - 1] - a[i - 1] + 1) / 2; } else now = b[i - 1]; now += a[i] - b[i - 1] + tim[i]; } printf("%d\n", now); } signed main() { int Case = read(); while(Case--) work(); return 0; }
B - Napoleon Cake
题意:
有一个n层的楼,对于每个\(i∈[1,n]\),有一个\(a_i\)表示从i往下数\(a_i\)个刷上了颜色,问每层楼有没有颜色。
题解:
当然可以用线段树,但是有\(O(n)\)的差分做法,而且代码量少。
因为不是在线询问,对于每个\(a_i\),在差分数组上打上标记就可以实现区间修改了,最后询问的时候求一下前缀和就行。
#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 2e5 + 1009; int n, a[N], f[N]; void work() { n = read(); for(int i = 0; i <= n; i++) f[i] = 0; for(int i = 1; i <= n; i++) { a[i] = read(); f[(i - a[i] + 1) > 0 ? (i - a[i] + 1) : 1] += 1; f[i + 1] -= 1; } for(int i = 1; i <= n; i++) { f[i] += f[i - 1]; if(f[i] > 0) printf("1 "); else printf("0 "); } printf("\n"); } signed main() { int Case = read(); while(Case--) work(); return 0; }
C - Going Home
题意:
给定一列数\(f\),要求找出四个不一样的\(a,b,c,d\)使得\(f_a + f_b=f_c + f_d\),如果没有输出\(no\)。
\(f_i\le 2.5\times 10^6\)
题解:
发现\(f_a + f_b\)是不超过\(5\times 10^6\)的,我们可以开个桶存下所有的\(f_a + f_b\),只要一个桶里面的数量大于4,那么一定有解。
证明:
有四对数,则情况有以下两种:
\(A\):\((x, y),(x, z), (z, y), (任意)\)这样子最后一对一定可以跟前面配对。
\(B\):\((x, y),(x, z), (x, p), (x, q)\)虽然这四个不能匹配,但是发现都\(f_x+f_i\)都等于一个数\(t\),也就是说明\(f_y=f_z=f_p=f_q\)这样就可以取\(f_y+f_z=f_p + f_q\)
于是,最多枚举\(2\times 10^7\)个数,一定存在答案。
暴力枚举就行了。
不过我考场上的做法是直接暴力,有可能被卡,谨慎参考以下代码。
#include <bits/stdc++.h> #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 5e6 + 1009; int n, a[N]; struct node { int x, y; }; vector<node> f[N]; signed main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int l = 1; l <= n; l++) { for(int i = 1; i + l <= n; i++) { int p = a[i] + a[i + l]; f[p].push_back((node){i, i + l}); for(int k = 0; k < f[p].size(); k++) { if(f[p][k].x == i || f[p][k].y == i) continue; if(f[p][k].x == i + l || f[p][k].y == i + l) continue; printf("YES\n"); printf("%d %d %d %d\n", f[p][k].x, f[p][k].y, i, i + l); return 0; } } } printf("NO\n"); return 0; }
D - Two chandeliers
题意:
两队人同时循环报数,每次报数的人假如颜色不一样就计一次贡献,问第\(k\)次贡献产生的时候报到了几。
两队个队伍内部没有重复颜色。
题解:
看官方Tutorial给的是CRT,但是我没有用CRT也水过去了。
考场上没看见颜色各不同结果挂了,要注意审题啊
我们先交换\(n,m\)使得\(m\le n\),这样之后我们可以考虑假如\(b\)数组第\(1\)个和\(a\)数组第\(i\)个同时开始报数,\(b\)报回到\(1\)产生几个贡献。
这是可以\(O(n)\)处理出来的,方法如下。
我们把\(b\)数组里面的数与下标进行映射,这样可以把\(b\)数组的颜色换成\(0, 1, 2, ..., m - 1\),然后考虑\(a\)数组内部的贡献。
\(a\)数组的第\(i\)位仅仅不会对从\(i - a[i]\)开始的匹配产生贡献,这样我们就可以统计出从第\(i\)个位置开始匹配,不能产生贡献的位置个数,\(m-cnt[i]\)就是产生的贡献个数。
这样结束后,我们一步的跨度就是\(m\)了,但是还是会超时,因为可能\(k\)很大,而\(m\)很小。
这时候我们考虑利用最小公倍数去优化枚举。
两列数匹配的循环节是最小公倍数,我们只需要计算最小公倍数匹配的贡献,然后剩下的部分暴力匹配就行了。
那么最小公倍数的匹配时间复杂度是多少呢?
最小公倍数最坏情况下为\(n\times m\),每次跨越\(m\),最多循环\(n\)步就可以求出来了。
接下来的就暴力了。
#include <bits/stdc++.h> #define int long long #define Mid (l + r << 1) #define lson (rt << 1) #define rson (rt << 1 | 1) using namespace std; int read() { char c; int num, f = 1; while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; while(c = getchar(), isdigit(c)) num = num * 10 + c - '0'; return f * num; } const int N = 5e5 + 1009; int n, m, k, a[N], b[N], f[N * 2]; int cnt[N], lcm; int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);} int cal(int k) { int q = 0, now = 1, p = 1, tot = 0; while(q < k) { if(q + cnt[now] >= k)break; q += cnt[now]; now = (now - 1 + m) % n + 1; tot += m; } while(q < k) { q += (a[now] != b[p]); now = now % n + 1; p = p % m + 1; tot++; } return tot; } int cal1(int k) { int tt = k / m, q = 0; for(int i = 1, now = 1; i <= tt; i++) { q += cnt[now]; now = (now - 1 + m) % n + 1; } return q; } signed main() { n = read(); m = read(); k = read(); if(n <= m) { swap(n, m); for(int i = 1; i <= m; i++) b[i] = read(); for(int i = 1; i <= n; i++) a[i] = read(); } else { for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= m; i++) b[i] = read(); } memset(f, -1, sizeof(f)); for(int i = 1; i <= m; i++) f[b[i]] = i - 1; for(int i = 1; i <= n; i++) { if(f[a[i]] != -1) { cnt[(i - f[a[i]] - 1 + n) % n + 1]++; } } for(int i = 1; i <= n; i++) cnt[i] = m - cnt[i]; int p = n / gcd(n, m) * m; int tt = cal1(p), ans = 0; ans += (k - 1) / tt * p; k = (k - 1) % tt + 1; ans += cal(k); printf("%lld\n", ans); return 0; } /* cnt[i]表示从i开始匹配,能有多少个匹配上 */