A. Pens and Pencils
签到。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 5; int a, b, c, d, k; void run() { cin >> a >> b >> c >> d >> k; for(int x = 1; x < k; x++) { int y = k - x; if(x * c >= a && y * d >= b) { cout << x << ' ' << y << '\n'; return; } } cout << -1 << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif int T; cin >> T; while(T--) run(); return 0; }
B. Rooms and Staircases
签到。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1005; int n; char s[N]; void run() { cin >> n; cin >> (s + 1); int left = -1, right; for(int i = 1; i <= n; i++) { if(s[i] == '1') { left = i; break; } } for(int i = n; i >= 1; i--) { if(s[i] == '1') { right = i; break; } } int ans = 0; if(left == -1) { ans = n; } else { ans = max(2 * right, 2 * (n - left + 1)); } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif int T; cin >> T; while(T--) run(); return 0; }
C. The Football Season
题意:
现在共有\(n,n\leq 10^{12}\)场比赛,赢一场比赛获得\(w\)分,平局获得\(d\)分,输了不加分,\(1\leq d<w\leq 10^5\)。
现在知道总分\(p,p\leq 10^{17}\),求三元组\((x,y,z)\)满足:
- \(x\cdot w+y\cdot d=p\)
- \(x+y+z=n\)
不存在则输出\(-1\)。
思路:
这不是扩欧模板题吗?也不是那么模板,还要贪心一下求个最小解,所以就卡了一整场比赛。
因为\(d<w\),那么可以知道\(y< w\),因为当\(y\geq w\)时,\(w\)个\(d\)不如\(x=d\)时,\(d\)个\(w\)划算。
然后\(w\)的范围也比较小,所以直接枚举判断就是了。
或者这样思考:反正是求较小解,那么得到解过后,\(y\)会模上\(w\),也就是说自然\(y\)的范围就比较小了。为什么是\(y\)模,因为\(x\)越多总数量越小~
感觉还是挺巧妙的QAQ,没有细心分析题目性质以及数据范围。
所以说模板题不可信!!Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 5; ll n, p, w, d; void run() { for(int y = 0; y < w; y++) { if(p - y * d < 0) break; if((p - y * d) % w == 0) { ll x = (p - y * d) / w; if(n - x - y >= 0) { cout << x << ' ' << y << ' ' << n - x - y << '\n'; return; } } } cout << -1 << '\n'; return; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> p >> w >> d) run(); return 0; }
D. Paint the Tree
容易发现只有链的情况合法,链的情况就很简单了。
因为颜色个数较少,所以直接枚举颜色排列染色就行。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 5; int n; int c[3][N]; int a[N], d[N], color[N], tmp[N]; vector <int> g[N]; int T; void dfs(int u, int fa) { a[++T] = u; for(auto v : g[u]) { if(v == fa) continue; dfs(v, u); } } void run() { for(int i = 1; i <= n; i++) { d[i] = 0; g[i].clear(); } T = 0; for(int i = 0; i < 3; i++) { for(int j = 1; j <= n; j++) { cin >> c[i][j]; } } for(int i = 1; i < n; i++) { int u, v; cin >> u >> v; ++d[u], ++d[v]; g[u].push_back(v), g[v].push_back(u); } for(int i = 1; i <= n; i++) { if(d[i] >= 3) { cout << -1 << '\n'; return; } } int rt; for(int i = 1; i <= n; i++) { if(d[i] == 1) rt = i; } dfs(rt, 0); int col[3] = {0, 1, 2}; ll ans = 1e18; do { ll res = 0; for(int i = 1; i <= n; i++) { res += c[col[(i - 1) % 3]][a[i]]; tmp[a[i]] = col[(i - 1) % 3]; } if(res < ans) { ans = res; memcpy(color, tmp, sizeof(color)); } } while(next_permutation(col, col + 3)); cout << ans << '\n'; for(int i = 1; i <= n; i++) cout << color[i] + 1 << " \n"[i == n]; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n) run(); return 0; }
E. Minimizing Difference
题意:
给出\(n\)个数,现在你可以执行不超过\(k\)次操作,每次操作可以让一个数加一或者减一。
问最后最大与最小值的差值最小是多少。
思路:
- 显然差值具有单调性,可以考虑二分差值。
- 注意到最终最优的情况中,一定存在某个数其没有发生改变。
- 那么直接枚举下界和上界,二分找另外一个界限就行(也可以利用双指针)。
至于为什么可以一定存在某个数不会改变,可以这样考虑:
若最终存在至少两个不同的数,那肯定满足;
若最终是只有一个值,那么一定会达到只有两个值的状态,我们固定一个数量较多的不变就行,不影响结果。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 5; ll n, k; int a[N]; ll sum[N]; bool chk(int d) { for(int i = 1; i <= n; i++) { int low = lower_bound(a + 1, a + n + 1, a[i]) - a - 1; int high = upper_bound(a + 1, a + n + 1, a[i] + d) - a; ll tot = 1ll * low * a[i] - sum[low] + sum[n] - sum[high - 1] - 1ll * (n - high + 1) * (a[i] + d); if(tot <= k) return true; } for(int i = 1; i <= n; i++) { int high = upper_bound(a + 1, a + n + 1, a[i]) - a; int low = lower_bound(a + 1, a + n + 1, a[i] - d) - a - 1; ll tot = 1ll * low * (a[i] - d) - sum[low] + sum[n] - sum[high - 1] - 1ll * (n - high + 1) * a[i]; if(tot <= k) return true; } return false; } void run() { for(int i = 1; i <= n; i++) cin >> a[i]; sort(a + 1, a + n + 1); for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i]; int l = 0, r = 1e9 + 1, mid; while(l < r) { mid = (l + r) >> 1; if(chk(mid)) r = mid; else l = mid + 1; } cout << l << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> k) run(); return 0; }
F. Chips
题意:
给出一个只有黑白两种颜色的环,然后执行\(k\)次操作,每次操作,对于每个位置,若其两边的颜色和它不同,它的颜色就发生改变。
问\(k\)次过后的颜色状态是什么。
注意一下,每次操作中,颜色改变是同时的。
思路:
关键在于连续的段,很容易发现颜色连续的段颜色不会发生改变。
那么我们将所有连续的段提取出来,每次单独考虑两个连续段的中间部分,将\(k\)和\(len\)取\(min\)暴力修改即可。
易知复杂度不超过\(O(n)\)。
注意一下因为是环,开头和结尾的部分不是很好处理,所以可以将序列复制两次,最后只看中间部分,这样就比较方便处理。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 2e5 + 5; int n, k; char s[3 * N]; struct seg{ int l, r, c; }a[N]; void run() { cin >> (s + 1); for(int j = n + 1; j <= 3 * n; j++) { s[j] = s[j - n]; } s[3 * n + 1] = '\0'; int tot = 0; for(int i = 1, j; i <= 3 * n; i = j) { j = i + 1; while(j <= 3 * n && s[i] == s[j]) ++j; int col = (s[i] == 'W' ? 0 : 1); if(j - i > 1) a[++tot] = {i, j - 1, col}; } // for(int i = 1; i <= tot; i++) { // cout << a[i].l << ' ' << a[i].r << '\n'; // } for(int i = 1; i < tot; i++) { int len = a[i + 1].l - 1 - a[i].r; if(a[i].c == a[i + 1].c) { if(len - 2 * k > 0) { for(int j = a[i].r + 1; j <= a[i].r + k; j++) { s[j] = (a[i].c == 0 ? 'W' : 'B'); } for(int j = a[i + 1].l - 1; j >= a[i + 1].l - k; j--) { s[j] = (a[i].c == 0 ? 'W' : 'B'); } for(int j = a[i].r + k + 1; j <= a[i + 1].l - k - 1; j++) { s[j] = (s[j - 1] == 'W' ? 'B' : 'W'); } } else { for(int j = a[i].r + 1; j <= a[i + 1].l - 1; j++) { s[j] = (a[i].c == 0 ? 'W' : 'B'); } } } else { if(len - 2 * k > 0) { for(int j = a[i].r + 1; j <= a[i].r + k; j++) { s[j] = (a[i].c == 0 ? 'W' : 'B'); } for(int j = a[i + 1].l - 1; j >= a[i + 1].l - k; j--) { s[j] = (a[i + 1].c == 0 ? 'W' : 'B'); } for(int j = a[i].r + k + 1; j <= a[i + 1].l - k - 1; j++) { s[j] = (s[j - 1] == 'W' ? 'B' : 'W'); } } else { for(int j = a[i].r + 1; j <= a[i].r + len / 2; j++) { s[j] = (a[i].c == 0 ? 'W' : 'B'); } for(int j = a[i + 1].l - 1; j >= a[i + 1].l - len / 2; j--) { s[j] = (a[i + 1].c == 0 ? 'W' : 'B'); } } } } if(tot == 0 && k & 1) { for(int i = n + 1; i <= 2 * n; i++) { s[i] = (s[i] == 'W' ? 'B' : 'W'); } } for(int i = n + 1; i <= 2 * n; i++) { cout << s[i]; } cout << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> k) run(); return 0; }
G. Running in Pairs
题意:
给出两个排列\(p,q\),先可以任意调换排列的顺序,求最大的\(\sum_{i=1}^{n}max(p_i,q_i)\),同时其不能超过\(k\)。
思路:
- 观察可以发现,在所有的\(max(p_i,q_i)\)中,同一个数最多出现两次。
- 采用贪心策略,应尽可能接近\(k\),所以每次将目前最大和最小的交换。
- 若交换过后值超过\(k\),因为交换产生的贡献是连续的,所以我们选取中间最优的进行交换即可。
这G貌似有点简单。。。就是一个普通的贪心。。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e6 + 5; ll n, k; int a[N], b[N]; void run() { ll ans = (n + 1) * n / 2; if(ans > k) { cout << -1 << '\n'; return; } for(int i = 1; i <= n; i++) a[i] = b[i] = i; int p = n / 2; int now = n - 1, pos = 1; for(int i = 1; i <= p; i++) { if(ans + now > k) { swap(a[pos], a[pos + k - ans]); break; } ans += now; now -= 2; swap(a[pos], a[n - pos + 1]); ++pos; } ll sum = 0; for(int i = 1; i <= n; i++) sum += max(a[i], b[i]); cout << sum << '\n'; for(int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n]; for(int i = 1; i <= n; i++) cout << b[i] << " \n"[i == n]; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> k) run(); return 0; }