Atcoder Grand Contest 001 题解
最近在刷AGC,就写一下题解。
A - BBQ Easy
sort完,每相邻两个组成一组,发现这样肯定是最优的。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } int n, L[210], ans; int main() { gi(n); for (int i = 1; i <= (n << 1); ++i) gi(L[i]); sort(L + 1, L + 2 * n + 1); for (int i = 1; i <= (n << 1); i += 2) ans += L[i]; printf("%d\n", ans); return 0; }
B - Mysterious Light
把光线分成很多平行四边形,所有平行四边形做法一样,只要递归下去就好了,只要某一边是另一边倍数就到达终点了,所以复杂度和gcd一样,是log的。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } long long ans = 0; void work(long long x, long long y) { if (x < y) { ans += (y / x) * x * 2 - x; if (y % x == 0) return; ans += x; work(x, y % x); } else { ans += (x / y) * y * 2 - y; if (x % y == 0) return; ans += y; work(y, x % y); } } int main() { long long N, X; cin >> N >> X; ans += N; work(X, N - X); cout << ans << endl; }
C - Shorten Diameter
枚举根,如果是偶数,所有深度要小于等于k/2,如果是奇数,则有一个根的儿子里所有点的深度可以最大为k/2+1,其他都为k/2。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } const int N = 2010; VI edge[N]; int n, k, dep[N], mxd; void dfs(int u, int fa, int d) { if (k & 1) if(d > k / 2 + 1) return; if (!(k & 1)) if (d > k / 2) return; ++dep[d]; mxd = max(mxd, d); for (auto v : edge[u]) if (v != fa) { dfs(v, u, d + 1); } } int main() { gii(n, k); for (int i = 1; i < n; ++i) { int u, v; gii(u, v); edge[u].pb(v); edge[v].pb(u); } int res = 0; for (int root = 1; root <= n; ++root) { int mx = 0, s = 1; for (auto son : edge[root]) { mxd = 0; dfs(son, root, 1); if (k & 1) mx = max(mx, dep[k / 2 + 1]); if (k & 1) s -= dep[k / 2 + 1]; for (int i = 1; i <= mxd; ++i) s += dep[i], dep[i] = 0; } res = max(res, s + mx); } printf("%d\n", n - res); return 0; }
D - Arrays and Palindrome
有一个结论:奇数不能超过2,否则无解,有解的话就把奇数放在两端,然后每次错1位放b就能保证他们全部相等了。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } int n, m, a[110]; bool comp(const int &i, const int &j) { return (i & 1) > (j & 1); } int b[110], ans; int main() { gii(n, m); int cnt = 0; for (int i = 1; i <= m; ++i) gi(a[i]); for (int i = 1; i <= m; ++i) if (a[i] & 1) ++cnt; if (cnt > 2) { puts("Impossible"); return 0; } sort(a + 1, a + m + 1, comp); if (a[2] & 1) swap(a[2], a[m]); for (int i = 1; i <= m; ++i) printf("%d ", a[i]); puts(""); if (m == 1) { if (a[1] - 1) printf("2\n%d %d \n", 1, a[1] - 1); else printf("1\n%d \n", 1); return 0; } if (a[1] - 1) b[++ans] = a[1] - 1; for (int i = 2; i < m; ++i) b[++ans] = a[i]; b[++ans] = a[m] + 1; printf("%d\n", ans); for (int i = 1; i <= ans; ++i) printf("%d ", b[i]); puts(""); return 0; }
E - BBQ Hard
首先答案是sigma(i<j) C(a[i]+a[j]+b[i]+b[j],a[i]+a[j]),一个C(a[i]+a[j]+b[i]+b[j],a[i]+a[j])可以看作从(-a[i],-b[i])走到(a[j],b[j])的方案数,所以我们所有dp一次性做了,减去一个点自己到自己,最后除以2。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } const int mod = 1e9 + 7; int n; int dp[4010][4010]; PII x[200010]; int& f(int x, int y) { return dp[x + 2005][y + 2005]; } int inc(int x, int y) { x += y; if (x >= mod) x -= mod; return x; } int dec(int x, int y) { x -= y; if (x < 0) x += mod; return x; } int fpow(int a, int x) { int ret = 1; for (; x; x >>= 1) { if (x & 1) ret = 1LL * ret * a % mod; a = 1LL * a * a % mod; } return ret; } int fac[8010], ifac[8010]; int C(int n, int m) { if (n < m || m < 0) return 0; return 1LL * fac[n] * ifac[m] % mod * ifac[n - m] % mod; } int main() { fac[0] = 1; for (int i = 1; i <= 8000; ++i) fac[i] = 1LL * fac[i - 1] * i % mod; ifac[8000] = fpow(fac[8000], mod - 2); for (int i = 8000; i; --i) ifac[i - 1] = 1LL * ifac[i] * i % mod; gi(n); for (int i = 1; i <= n; ++i) gii(x[i].fi, x[i].se), ++f(-x[i].fi, -x[i].se); for (int i = -2000; i <= 2000; ++i) for (int j = -2000; j <= 2000; ++j) f(i, j) = inc(f(i, j), f(i - 1, j)), f(i, j) = inc(f(i, j), f(i, j - 1)); int ans = 0; for (int i = 1; i <= n; ++i) ans = inc(ans, f(x[i].fi, x[i].se)); for (int i = 1; i <= n; ++i) ans = dec(ans, C((x[i].fi + x[i].se) << 1, x[i].fi << 1)); ans = 1LL * ans * fpow(2, mod - 2) % mod; printf("%d\n", ans); return 0; }
F - Wide Swap
我们可以令q[p[i]]=i,那么就是相邻的如果|q[i]-q[i+1]|>=k就能交换,所以相对顺序不变,也就是|i-j|<k,p[i]<p[j]是固定的。如果i向j连边就是拓扑排序问题,边是n^2的就线段树优化一下。
//waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<int, int> PII; typedef vector<int> VI; typedef long long int64; typedef unsigned int uint; typedef unsigned long long uint64; #define gi(x) ((x) = F()) #define gii(x, y) (gi(x), gi(y)) #define giii(x, y, z) (gii(x, y), gi(z)) int F() { char ch; int x, a; while (ch = getchar(), (ch < '0' || ch > '9') && ch != '-'); if (ch == '-') ch = getchar(), a = -1; else a = 1; x = ch - '0'; while (ch = getchar(), ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0'; return a * x; } const int N = 5e5 + 10; int n, k, p[N], q[N]; int t[N << 2], cnt; int get(int x, int y) { if (!x) return y; if (!y) return x; return min(x, y); } void modify(int x, int v) { t[x += cnt] = v; for (x >>= 1; x; x >>= 1) t[x] = get(t[x << 1], t[x << 1 | 1]); } int query(int l, int r) { int ans = 0; l = max(l, 1); r = min(r, n); if (l > r) return 0; for (l += cnt - 1, r += cnt + 1; l ^ r ^ 1; l >>= 1, r >>= 1) { if (~l & 1) ans = get(ans, t[l ^ 1]); if ( r & 1) ans = get(ans, t[r ^ 1]); } return ans; } vector<int> edge[N]; int deg[N]; priority_queue<int> pq; int ans[N]; int main() { gii(n, k); for (int i = 1; i <= n; ++i) gi(p[i]), q[p[i]] = i; for (cnt = 1; cnt < n + 2; cnt <<= 1); --cnt; for (int i = n; i; --i) { int x = q[query(q[i] - k + 1, q[i] - 1)]; if (x) edge[q[i]].pb(x), ++deg[x]; x = q[query(q[i] + 1, q[i] + k - 1)]; if (x) edge[q[i]].pb(x), ++deg[x]; modify(q[i], i); } for (int i = 1; i <= n; ++i) if (!deg[i]) pq.push(-i); int cur = 0; while (!pq.empty()) { int u = -pq.top(); pq.pop(); ans[u] = ++cur; for (auto v : edge[u]) { --deg[v]; if (!deg[v]) pq.push(-v); } } for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]); return 0; }