A. Diplomas and Certificates
点击查看代码
void solve() {i64 n, k;std::cin >> n >> k;i64 cnt = (n / 2) / (k + 1) * k;std::cout << cnt / k << " " << cnt << " " << n - cnt - cnt / k << "\n";
}
B. Permutation Game
题意:\(n\)个人围成一个圈,如果第\(i\)个人是领导者,那么下一个领导者是顺时针走\(a_i\)个位置。其中\(a\)是一个排列。选择给出\(m\)轮的领导者,求\(a\)。
可以通过\(l_{i+1} - l_i\)得出\(a_i\),检查有没有冲突和重复,没有出现过的随便填没用过的数。
点击查看代码
void solve() {int n, m;std::cin >> n >> m;std::vector<int> l(m + 1);for (int i = 1; i <= m; ++ i) {std::cin >> l[i];}std::vector<int> a(n + 1);std::set<int> s;for (int i = 1; i <= n; ++ i) {s.insert(i);}for (int i = 1; i + 1 <= m; ++ i) {int t = (l[i + 1] - l[i] + n) % n;if (t == 0) {t = n;}if (a[l[i]] == 0) {if (!s.count(t)) {std::cout << -1 << "\n";return;}s.erase(t);a[l[i]] = t;} else if (a[l[i]] != t) {std::cout << -1 << "\n";return;}}for (int i = 1; i <= n; ++ i) {if (a[i] == 0) {a[i] = *s.begin();s.erase(s.begin());}}for (int i = 1; i <= n; ++ i) {std::cout << a[i] << " \n"[i == n];}
}
C. Sofa Thief
题意:\(n\)个沙发,每个沙发有两个坐标,对于\(i, j\),如果\(j\)有一个横坐标小于\(i\)的一个横坐标,则\(j\)在\(i\)的左边。如果\(j\)有一个横坐标大于\(i\)的一个横坐标,则\(j\)在\(i\)的右边。如果\(j\)有一个纵坐标小于\(i\)的一个纵坐标,则\(j\)在\(i\)的上边。如果\(j\)有一个纵坐标大于\(i\)的一个纵坐标,则\(j\)在\(i\)的下边。
求有没有一个沙发的左边有\(cnt_l\)个沙发,右边有\(cnt_r\)个沙发,上面有\(cnt_t\)个沙发,下面有\(cnt_d\)个沙发。
给每个坐标从小到大从大到小排序模拟,求出每个沙发各个方向有多少沙发。
点击查看代码
void solve() {int k, n, m, L, R, T, D;std::cin >> k >> n >> m;std::vector<std::array<int, 3>> a;for (int i = 0; i < k; ++ i) {int x1, y1, x2, y2;std::cin >> x1 >> y1 >> x2 >> y2;a.push_back({x1, y1, i});a.push_back({x2, y2, i});}std::cin >> L >> R >> T >> D;std::vector<int> l(k, -1), r(k, -1), t(k, -1), d(k, -1);std::sort(a.begin(), a.end());std::set<int> s;for (int i = 0; i < a.size(); ++ i) {int j = i;while (j + 1 < a.size() && a[j + 1][0] == a[i][0]) {++ j;}for (int x = i; x <= j; ++ x) {l[a[x][2]] = (int)s.size() - s.count(a[x][2]);}for (int x = i; x <= j; ++ x) {s.insert(a[x][2]);}i = j;}s.clear();std::sort(a.begin(), a.end(), std::greater<>());for (int i = 0; i < a.size(); ++ i) {int j = i;while (j + 1 < a.size() && a[j + 1][0] == a[i][0]) {++ j;}for (int x = i; x <= j; ++ x) {r[a[x][2]] = (int)s.size() - s.count(a[x][2]);}for (int x = i; x <= j; ++ x) {s.insert(a[x][2]);}i = j;}s.clear();std::sort(a.begin(), a.end(), [&](std::array<int, 3> & a, std::array<int, 3> & b) {return a[1] > b[1];});for (int i = 0; i < a.size(); ++ i) {int j = i;while (j + 1 < a.size() && a[j + 1][1] == a[i][1]) {++ j;}for (int x = i; x <= j; ++ x) {d[a[x][2]] = (int)s.size() - s.count(a[x][2]);}for (int x = i; x <= j; ++ x) {s.insert(a[x][2]);}i = j;}s.clear();std::sort(a.begin(), a.end(), [&](std::array<int, 3> & a, std::array<int, 3> & b) {return a[1] < b[1];});for (int i = 0; i < a.size(); ++ i) {int j = i;while (j + 1 < a.size() && a[j + 1][1] == a[i][1]) {++ j;}for (int x = i; x <= j; ++ x) {t[a[x][2]] = (int)s.size() - s.count(a[x][2]);}for (int x = i; x <= j; ++ x) {s.insert(a[x][2]);}i = j;}s.clear();for (int i = 0; i < k; ++ i) {// std::cout << l[i] << " " << r[i] << " " << t[i] << " " << d[i] << "\n";if (l[i] == L && r[i] == R && t[i] == T && d[i] == D) {std::cout << i + 1 << "\n";return;}}std::cout << -1 << "\n";
}
D. Multicolored Cars
题意:有一个数组,\(cntx_i\)为\(x\)这个数在\(1\)到\(i\)出现的次数。现在\(Alcie\)选了一个\(A\),你要选一个\(B\),使得对于任意一个\(i\)都有\(cntB_i \geq cntA_i\)。
用\(set\)存每个数的出现次数,然后从前往后更新\(cnt\),同时更新\(set\)。然后每次把小于\(cntA_i\)的位置都删掉。每次二分是\(log\)的,而每个数只会被删一次,总共是\(O(n)\)的,所以时间复杂度是\(O(nlogV)\),\(V\)是值域。
点击查看代码
void solve() {int n, A;std::cin >> n >> A;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}std::set<std::pair<int, int>> s;for (int i = 1; i <= 1000000; ++ i) {s.insert({0, i});}std::vector<int> cnt(1000010);for (auto & c : a) {if (s.count({cnt[c], c})) {s.erase({cnt[c], c});++ cnt[c];s.insert({cnt[c], c});}auto it = s.lower_bound({cnt[A], 0});s.erase(s.begin(), it);}s.erase({cnt[A], A});if (s.empty()) {std::cout << -1 << "\n";} else {std::cout << s.begin()->second << "\n";}
}
E. Card Game Again
题意:求有多少区间的乘积和是\(k\)的倍数。
对\(k\)质因数分解,得到\(m\)个质数及其个数,然后记\(sum[i][j]\)为\([1, i]\)里第\(j\)个质数出现的次数。那么我们枚举右端点,二分合法的最右边的左端点。
点击查看代码
void solve() {int n, k;std::cin >> n >> k;std::vector<int> a(n + 1);for (int i = 1; i <= n; ++ i) {std::cin >> a[i];}std::map<int, int> mp;for (int i = 2; i <= k / i; ++ i) {if (k % i == 0) {while (k % i == 0) {++ mp[i];k /= i;}}}if (k > 1) {++ mp[k];}std::vector<int> sumk;std::vector<int> b;for (auto & [x, cnt] : mp) {b.push_back(x);sumk.push_back(cnt);}int m = (int)b.size();std::vector sum(n + 1, std::vector<int>(m));for (int i = 1; i <= n; ++ i) {sum[i] = sum[i - 1];for (int j = 0; j < m; ++ j) {int cnt = 0;while (a[i] % b[j] == 0) {++ cnt;a[i] /= b[j];}sum[i][j] += cnt;}}auto check = [&](int l, int r) -> bool {for (int i = 0; i < m; ++ i) {if (sum[r][i] - sum[l - 1][i] < sumk[i]) {return false;}}return true;};i64 ans = 0;for (int i = 1; i <= n; ++ i) {int l = 1, r = i;while (l < r) {int mid = l + r + 1 >> 1;if (check(mid, i)) {l = mid;} else {r = mid - 1;}}if (!check(l, i)) {l = 0;}ans += l;}std::cout << ans << "\n";
}
F. Level Generation
待补
G. Four Melodies
题意:在一个数组种选四个不相交的子序列,每个子序列相邻的两个数要么相差1,要么余7相同。使得四个子序列长度加起来最大。
本质是在一个图里选\(k\)条路径使得路径和最大的问题。
如果我们向每个\(|a_i - a_j| = 1\)或\(a_i \equiv a_j \pmod{7}\)的\((i, j)\)由\(i\)向\(j\)连边。那么就等价于求图里的两段路径,使得路径长度之和最大,这是个经典费用流问题。我们把每个点拆成出度和入度,一个点的入度向出度连费用为1流量为1的边,可以连边的由出度向入度连费用为0流量为1的边,同时源点给每个点的入度连费用为0流量为1的边,每个点的出度向汇点连费用为0流量为1的边,然后为了满足两条路径的需求,超级源点向源点连费用为0流量为2的边,汇点向超级汇点连费用为0流量为4的边。但这样边数太大,时间复杂度不能接受。
我们可以对于每个\(i\)的出度,往右边最近的\(a_i - a_j = 1\)以及\(a_i - a_j = -1\)的\(j\)的入度连边,因为满足条件的\(a_j\)值只有两种,我们选最近的可以给后面留出更大空间,显然更优。然后对于\(a_i \equiv a_j \pmod{7}\)的最近的\(j\),我们由\(i\)的出度向\(j\)的入度连边,同时\(i\)的入度向\(j\)的入度连边,这意味着选了\(i\)可以继续选\(j\),或者如果不选\(i\)下一个就可以选\(j\)。这样就解决了。
这个问题可以由\(4\)条路径扩展成\(k\)条路径,只需要该超级源点和超级汇点与源点汇点之间的流量。
点击查看代码
template<class T>
struct MinCostFlow {struct _Edge {int to;T cap;T cost;_Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {}};int n;std::vector<_Edge> e;std::vector<std::vector<int>> g;std::vector<T> h, dis;std::vector<int> pre;bool dijkstra(int s, int t) {dis.assign(n, std::numeric_limits<T>::max());pre.assign(n, -1);std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> que;dis[s] = 0;que.emplace(0, s);while (!que.empty()) {T d = que.top().first;int u = que.top().second;que.pop();if (dis[u] != d) {continue;}for (int i : g[u]) {int v = e[i].to;T cap = e[i].cap;T cost = e[i].cost;if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) {dis[v] = d + h[u] - h[v] + cost;pre[v] = i;que.emplace(dis[v], v);}}}return dis[t] != std::numeric_limits<T>::max();}MinCostFlow() {}MinCostFlow(int n_) {init(n_);}void init(int n_) {n = n_;e.clear();g.assign(n, {});}void addEdge(int u, int v, T cap, T cost) {g[u].push_back(e.size());e.emplace_back(v, cap, cost);g[v].push_back(e.size());e.emplace_back(u, 0, -cost);}std::pair<T, T> flow(int s, int t) {T flow = 0;T cost = 0;h.assign(n, 0);while (dijkstra(s, t)) {for (int i = 0; i < n; ++i) {h[i] += dis[i];}T aug = std::numeric_limits<int>::max();for (int i = t; i != s; i = e[pre[i] ^ 1].to) {aug = std::min(aug, e[pre[i]].cap);}for (int i = t; i != s; i = e[pre[i] ^ 1].to) {e[pre[i]].cap -= aug;e[pre[i] ^ 1].cap += aug;}flow += aug;cost += aug * h[t];}return std::make_pair(flow, cost);}struct Edge {int from;int to;T cap;T cost;T flow;};std::vector<Edge> edges() {std::vector<Edge> a;for (int i = 0; i < e.size(); i += 2) {Edge x;x.from = e[i + 1].to;x.to = e[i].to;x.cap = e[i].cap + e[i + 1].cap;x.cost = e[i].cost;x.flow = e[i + 1].cap;a.push_back(x);}return a;}
};void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}MinCostFlow<int> mf(2 * n + 4);int s = 2 * n, t = 2 * n + 1, S = 2 * n + 2, T = 2 * n + 3;mf.addEdge(S, s, 4, 0);mf.addEdge(t, T, 4, 0);for (int i = 0; i < n; ++ i) {mf.addEdge(s, i, 1, 0);mf.addEdge(i + n, t, 1, 0);mf.addEdge(i, i + n, 1, -1);for (int j = i + 1; j < n; ++ j) {if (a[i] == a[j] + 1) {mf.addEdge(i + n, j, 1, 0);break;}}for (int j = i + 1; j < n; ++ j) {if (a[i] == a[j] - 1) {mf.addEdge(i + n, j, 1, 0);break;}}for (int j = i + 1; j < n; ++ j) {if (a[i] % 7 == a[j] % 7) {mf.addEdge(i + n, j, 1, 0);mf.addEdge(i, j, 1, 0);break;}}}std::cout << -mf.flow(S, T).second << "\n";
}