整出来了七道题,ABC最后都没做赛后补的
A题我和题解以及大部分人思路都不一样,并没有用到树状数组,我甚至看到有笛卡尔树做的(并不会),我是用了两次单调栈,维护两边最远的位置,再算贡献,然后这样写好像是代码长度最短的,应该是最简单的方法吧,但是这么写细节上也有很多坑,比如最小值不在区间内,在端点上时要特殊处理
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; int a[N], x[N]; ll f[N][20], ans[N], dp[N], pre[N]; ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } inline void cal(ll a, ll b) { ll p = gcd(a, b); a /= p; b /= p; printf("%lld/%lld\n", a, b); } vector < int > v; stack < int > s; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); f[i][0] = a[i]; } for (int j = 1; j <= 20; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); int m; scanf("%d", &m); while (m--) { int k; scanf("%d", &k); for (int i = 1; i <= k; i++) { scanf("%d", &x[i]); ans[a[x[i]]]++; v.push_back(a[x[i]]); } sort(x + 1, x + k + 1); s.push(0); dp[0] = (1 << 30); for (int i = 1; i < k; i++) { int l = log2(x[i + 1] - x[i] + 1); ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]); dp[i] = q; while (!s.empty() && dp[s.top()] <= q) s.pop(); pre[i] = (i - s.top()); //ans[q] += 1ll * (i - s.top()); s.push(i); } while (!s.empty()) s.pop(); s.push(k); dp[k] = (1 << 30); for (int i = k - 1; i >= 1; i--) { int l = log2(x[i + 1] - x[i] + 1); ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]); dp[i] = q; while (!s.empty() && dp[s.top()] <= q) s.pop(); if (dp[i] == dp[i + 1]) ans[q] += 2 * pre[i]; else ans[q] += 1ll * 2 * (s.top() - i) * pre[i]; v.push_back(q); s.push(i); } while (!s.empty()) s.pop(); sort(v.begin(), v.end()); for (int i = 0; i < v.size(); i++) { if (i != 0 && v[i] == v[i - 1]) continue; printf("%d ", v[i]); cal(ans[v[i]], 1ll * k * k); ans[v[i]] = 0; } v.clear(); } return 0; }
C题很简单,但是有一个坑就是长度大于l/2时候要判断,刚开始没有注意到这个,WA了好多次
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 10; int a[N], x[N]; ll f[N][20], ans[N], dp[N], pre[N]; ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } inline void cal(ll a, ll b) { ll p = gcd(a, b); a /= p; b /= p; printf("%lld/%lld\n", a, b); } vector < int > v; stack < int > s; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); f[i][0] = a[i]; } for (int j = 1; j <= 20; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); int m; scanf("%d", &m); while (m--) { int k; scanf("%d", &k); for (int i = 1; i <= k; i++) { scanf("%d", &x[i]); ans[a[x[i]]]++; v.push_back(a[x[i]]); } sort(x + 1, x + k + 1); s.push(0); dp[0] = (1 << 30); for (int i = 1; i < k; i++) { int l = log2(x[i + 1] - x[i] + 1); ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]); dp[i] = q; while (!s.empty() && dp[s.top()] <= q) s.pop(); pre[i] = (i - s.top()); //ans[q] += 1ll * (i - s.top()); s.push(i); } while (!s.empty()) s.pop(); s.push(k); dp[k] = (1 << 30); for (int i = k - 1; i >= 1; i--) { int l = log2(x[i + 1] - x[i] + 1); ll q = max(f[x[i]][l], f[x[i + 1] - (1 << l) + 1][l]); dp[i] = q; while (!s.empty() && dp[s.top()] <= q) s.pop(); if (dp[i] == dp[i + 1]) ans[q] += 2 * pre[i]; else ans[q] += 1ll * 2 * (s.top() - i) * pre[i]; v.push_back(q); s.push(i); } while (!s.empty()) s.pop(); sort(v.begin(), v.end()); for (int i = 0; i < v.size(); i++) { if (i != 0 && v[i] == v[i - 1]) continue; printf("%d ", v[i]); cal(ans[v[i]], 1ll * k * k); ans[v[i]] = 0; } v.clear(); } return 0; }
B题应该是最有意思的一道题了,并没有想出来怎么做,看到题解的时候感叹自己怎么这么菜,简单来说就是对每个状态建一个最短路树,然后像分层图一样连接起来,dfs一遍就可以了
记一下自己的很多坑,一直只通过了90%
1. 编号最小,需要用dis数组记录判断
2. 每次可以操纵多个开关
写的不是很优雅
#include <bits/stdc++.h> using namespace std; const int N = 110, M = 110 * (1 << 11); int p[11]; int n, m, k; #define debug(x) cout << #x << " " << x << endl; vector < int > edge[N], G[M]; inline void add(int u, int v) { edge[u].push_back(v); } int vis[M]; bool col[M][2]; queue < int > Q; inline void build(int flag) { int cnt = 0, b = flag; while (b) { cnt++; if (b & 1) vis[p[cnt] + flag * n] = (1 << 30); b >>= 1; } for (int j = 0; j < (1 << k); j++) if (j != flag) { for (int i = 1; i <= n; i++) G[j * n + i].push_back(flag * n + i); } if (vis[n + flag * n] == (1 << 30)) return ; Q.push(n); vis[n + flag * n] = 0; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; if (vis[v + flag * n] > vis[u + flag * n] + 1) { G[v + flag * n].push_back(u + flag * n); if (vis[v + flag * n] != (1 << 30)) Q.push(v); vis[v + flag * n] = vis[u + flag * n] + 1; } else if (vis[v + flag * n] == vis[u + flag * n] + 1) G[v + flag * n].back() = min(G[v + flag * n].back(), u + flag * n); } } } inline bool check(int u, int v) { return (u - 1) / n == (v - 1) / n; } stack < int > s; bool dfs(int u, int flag) { bool tmp = false; col[u][flag] = true; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (check(u, v) || !flag) { if (!col[v][!check(u, v)] && dfs(v, !check(u, v))) { s.push(u); return true; } tmp = true; } } if (!tmp && u % n != 0) { s.push(u); return true; } return false; } int main() { scanf("%d %d %d", &n, &m, &k); for (int i = 1; i <= k; i++) scanf("%d", &p[i]); for (int i = 1; i <= m; i++) { int u, v; scanf("%d %d", &u, &v); add(u, v); add(v, u); } memset(vis, 0x3f, sizeof(vis)); for (int i = 0; i < (1 << k); i++) build(i); //if (!dfs(1, 0)) return 0; dfs(1, 0); int last = (s.top() - 1) / n; s.pop(); while (!s.empty()) { int u = s.top(); s.pop(); u = (u - 1) / n; int k = last ^ u, cnt = 0; if (k == 0) printf("continue\n"); else { while (k) { cnt++; if (k & 1) { if (u & (1 << (cnt - 1))) printf("lock %d\n", p[cnt]); else printf("unlock %d\n", p[cnt]); } k >>= 1; } } last = u; } puts("checkmate!"); return 0; }