Codeforces Round #669 (Div. 2)
A. Ahahahahahahahaha
題意:
給定一個長度為 \(n,n\leq 10^3\)並且 \(n\) 為偶數的 \(01\) 序列。
現在去掉最多 \(\frac{n}{2}\) 個元素,使得剩下的序列奇數位置的和減去偶數位置的和為 \(0\)。
思路:
如果 \(0\) 的個數超過一半,那麼留下所有 \(0\) 即可。
否則要考慮留下一些 \(1\),並且這些 \(1\) 一定是成對出現的。考慮連續的三個數只有可能這幾種情況:\(001,010,011\),始終是能夠選出一對出來,也就是每三個數會選兩個出來,那麼直接這樣選直到滿足條件即可。
題解的做法是根據大小關係直接構造全 \(0\) 或者全 \(1\)
Code
// Author : heyuhhh // Created Time : 2020/09/08 22:50:25 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; vector<int> b; for (int i = 0; i < n; i++) { int j = i + 1, k = i + 2; if (j < n && a[i] == a[j]) { ++i; b.emplace_back(a[i]); b.emplace_back(a[i]); } else if (j < n && k < n) { i = k; b.emplace_back(a[i]); b.emplace_back(a[i]); } else if (j == n) { if (a[i] == 0) b.emplace_back(a[i]); } else if (k == n) { b.emplace_back(0); break; } } cout << sz(b) << '\n'; for (auto it : b) cout << it << ' '; cout << '\n'; } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
B. B. Big Vova
貪心構造就行。時間複雜度 \(O(n^2logn)\)。
Code
// Author : heyuhhh // Created Time : 2020/09/08 22:39:15 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; sort(a.rbegin(), a.rend()); vector<int> b(n); vector<bool> chk(n); int g = 0; for (int i = 0; i < n; i++) { int now = -1, p; for (int j = 0; j < n; j++) if (!chk[j]) { if (__gcd(g, a[j]) > now) { now = __gcd(g, a[j]); p = j; } } chk[p] = true; b[i] = a[p]; g = __gcd(g, a[p]); } for (int i = 0; i < n; i++) cout << b[i] << " \n"[i == n - 1]; } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
C. Chocolate Bunny
題意:
互動題。
現在有一個排列 \(p\),但是隻知道長度。
然後最多 \(2n\) 次詢問,每次詢問 \((x,y)\),會回答 \(p_x\% p_y\) 的值。
最後要還原排列。
思路:
詢問 \((x,y),(y,x)\) 即可得到較小的那個數,也就是每兩次詢問能確定一個數,那麼通過 \(2(n-1)\) 次詢問能夠確定 \(n-1\) 個數,剩下一個數可以直接確定。
D. Discrete Centrifugal Jumps
題意:
現在有 \(n\) 根柱子,第 \(i\) 根的高度為 \(h_i\)。
現在能從 \(i\) 跳到 \(j\),當且僅當:
- \(j=i+1\);
- \(max(h_{i+1},\cdots,h_{j-1})<min(h_i,h_j)\);
- \(min(h_{i+1},\cdots,h_{j-1})<max(h_i,h_j)\).
問最少多少跳能從 \(1\) 到 \(n\)。
思路:
第一種情況不用考慮,主要來考慮第二、三種情況:
對於一個 \(i\),能跳到的位置一定是一個遞增的序列,並且最右邊的位置是第一個高度大於等於 \(h_i\) 的位置,任意證明再往後就不合法了。除開最後一項,前面的一定要滿足他們的高度要小於 \(h_i\),即 \(h_i\) 是第一個高度大於他們的。
第三種情況就是一個遞減序列,最右邊是第一個小於等於 \(h_i\) 的位置,並且 \(h_i\) 是第一個小於他們的。
但只有小於會出問題,因為可能出現多個數相等的情況,這樣只能從前面第一個小於等於/大於等於的地方轉移過來。
通過單調棧預處理這些資訊,然後直接 \(dp\) 即可,轉移是常數級的,所以複雜度為 \(O(n)\)。
Code
// Author : heyuhhh
// Created Time : 2020/09/09 00:21:00
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++)
cin >> a[i];
vector<int> sta(n);
int top = -1;
vector<int> big(n);
for (int i = 0; i < n; i++) {
while (top >= 0 && a[sta[top]] <= a[i])
big[sta[top--]] = i;
sta[++top] = i;
}
while (top >= 0)
big[sta[top--]] = -1;
vector<int> small(n);
for (int i = 0; i < n; i++) {
while (top >= 0 && a[sta[top]] >= a[i])
small[sta[top--]] = i;
sta[++top] = i;
}
while (top >= 0)
small[sta[top--]] = -1;
vector<int> lbig(n);
for (int i = n - 1; i >= 0; i--) {
while (top >= 0 && a[sta[top]] <= a[i])
lbig[sta[top--]] = i;
sta[++top] = i;
}
while (top >= 0)
lbig[sta[top--]] = -1;
vector<int> lsmall(n);
for (int i = n - 1; i >= 0; i--) {
while (top >= 0 && a[sta[top]] >= a[i])
lsmall[sta[top--]] = i;
sta[++top] = i;
}
while (top >= 0)
lsmall[sta[top--]] = -1;
vector<vector<int>> G(n);
for (int i = 0; i < n; i++) {
if (lsmall[i] != -1)
G[i].push_back(lsmall[i]);
if (lbig[i] != -1)
G[i].push_back(lbig[i]);
if (small[i] != -1)
G[small[i]].push_back(i);
if (big[i] != -1)
G[big[i]].push_back(i);
}
vector<int> dp(n, INF);
dp[0] = 0;
for (int i = 1; i < n; i++) {
for (auto j : G[i]) {
dp[i] = min(dp[i], dp[j] + 1);
}
}
int res = dp[n - 1];
cout << res << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
題意:
給定一個 \(n\) 個點,\(m\) 條邊的有向圖,現在要給每個結點黑白染色,從一個點出發,只能走對應顏色的出邊。
現在問怎麼染色能使得 \(1\rightarrow n\) 的最短路儘可能長。
思路:
樸素想法即是從起點開始貪心染色,選擇一個距離最短的出點,然後將當前點染為和該邊顏色不同的顏色,這樣就能避免走最短路。但是實際上程式碼中很複雜,因為可能會涉及回溯等問題。
實際上直接在反向圖上面染色即可,從終點往前bfs進行染色。
假設當前點為 \(v\),那麼對於 \((u,v,t)\) 這樣的邊,如果 \(u\) 點未染色就染為另一種顏色,否則距離加 \(1\) 並且入隊。這樣能保證最優性,因為假設存在另外一點 \(v'\) 能夠到 \(u\),但是在後面出隊,說明距離更大,那麼染色過後最短路可以從 \(u\rightarrow v\) 這條路過來。
執行一遍上述演算法即可得到最長的最短距離以及染色方案。
Code
// Author : heyuhhh
// Created Time : 2020/09/09 11:25:07
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
int n, m;
cin >> n >> m;
vector<vector<pii>> G(n);
for (int i = 0; i < m; i++) {
int u, v, t;
cin >> u >> v >> t;
--u, --v;
G[v].emplace_back(u, t);
}
vector<int> col(n, -1), dis(n, INF);
vector<bool> vis(n);
queue<int> q;
dis[n - 1] = col[n - 1] = 0;
q.push(n - 1);
while (!q.empty()) {
int v = q.front(); q.pop();
vis[v] = true;
for (auto& it : G[v]) {
int u = it.fi, t = it.se;
if (vis[u]) continue;
if (col[u] == -1) {
col[u] = 1 - t;
} else {
if (col[u] == t && dis[u] > dis[v] + 1) {
dis[u] = dis[v] + 1;
q.push(u);
}
}
}
}
int ans = dis[0];
if (ans == INF) ans = -1;
cout << ans << '\n';
for (int i = 0; i < n; i++)
cout << max(0, col[i]);
cout << '\n';
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}