Идея: MisterGu
Разбор
Tutorial is loading...
Решение
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while(t--) {
string n;
cin >> n;
if((n.back() - '0') % 2 == 0) {
cout << "0\n";
continue;
}
if((n[0] - '0') % 2 == 0) {
cout << "1\n";
continue;
}
int count_2 = count(n.begin(), n.end(), '2');
int count_4 = count(n.begin(), n.end(), '4');
int count_6 = count(n.begin(), n.end(), '6');
int count_8 = count(n.begin(), n.end(), '8');
if(count_2 > 0 || count_4 > 0 || count_6 > 0 || count_8 > 0) {
cout << "2\n";
continue;
}
cout << "-1\n";
}
return 0;
}
1611B - Составление команд: программисты и математики
Идея: MikeMirzayanov
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
ll a, b;
cin >> a >> b;
cout << min(min(a, b), (a + b) / 4) << '\n';
}
int main() {
int t;
cin >> t;
for (int i = 0; i < t; ++i) {
solve();
}
return 0;
}
1611C - Поликарп восстанавливает перестановку
Идея: MikeMirzayanov
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
using namespace std;
#define forn(i, n) for (int i = 0; i < int(n); i++)
int main() {
int t;
cin >> t;
forn(tt, t) {
int n;
cin >> n;
vector<int> a(n);
forn(i, n)
cin >> a[i];
if (a[0] != n && a[n - 1] != n)
cout << -1 << endl;
else {
for (int i = n - 1; i >= 0; i--)
cout << a[i] << " ";
cout << endl;
}
}
}
1611D - Расстановка весов ребер корневого дерева
Идея: MikeMirzayanov
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin >> n;
vector<int> b(n + 1), p(n + 1), dist(n + 1, -1);
for(int i = 1; i <= n; i++)
cin >> b[i];
for(int i = 1; i <= n; i++)
cin >> p[i];
if (b[p[1]] != p[1]){
cout << -1 << '\n';
return;
}
dist[p[1]] = 0;
for(int i = 2; i <= n; i++){
if(dist[b[p[i]]] == -1){
cout << -1 << '\n';
return;
}
dist[p[i]] = dist[p[i - 1]] + 1;
}
for(int i = 1; i <= n; i++) {
cout << dist[i] - dist[b[i]] << ' ';
}
cout << '\n';
}
int main() {
int t;
cin >> t;
while(t-- > 0) {
solve();
}
}
1611E1 - Побег из лабиринта (простая версия)
Идея: Vladosiya
Разбор
Tutorial is loading...
Решение
//
// Created by Vlad on 16.11.2021.
//
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define x first
#define y second
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
typedef long double ld;
typedef long long ll;
using namespace std;
mt19937 rnd(143);
const int inf = 1e10;
const int M = 998244353;
const ld pi = atan2(0, -1);
const ld eps = 1e-4;
void solve() {
int n, k;
cin >> n >> k;
vector<int> color(n, -1);
deque<int> q;
for(int i = 0; i < k; ++i){
int x;
cin >> x;
color[--x] = 0;
q.push_back(x);
}
color[0] = 1;
q.push_back(0);
vector<vector<int>> g(n);
for(int i = 0; i < n - 1; ++i){
int u, v;
cin >> u >> v;
g[--u].push_back(--v);
g[v].push_back(u);
}
while(!q.empty()){
int v = q.front();
q.pop_front();
for(int u: g[v]){
if(color[u] == -1){
color[u] = color[v];
q.push_back(u);
}
}
}
for(int v = 1; v < n; ++v){
if(g[v].size() == 1 && color[v] == 1){
cout << "YES";
return;
}
}
cout << "NO";
}
bool multi = true;
signed main() {
int t = 1;
if (multi) {
cin >> t;
}
for (; t != 0; --t) {
solve();
cout << "\n";
}
return 0;
}
1611E2 - Побег из лабиринта (сложная версия)
Идея: Vladosiya
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define x first
#define y second
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
/*#pragma GCC optimize("Ofast")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,popcnt,abm,mmx,tune=native")
#pragma GCC optimize("fast-math")
*/
typedef long double ld;
typedef long long ll;
using namespace std;
mt19937 rnd(143);
const int inf = 1e10;
const int M = 998244353;
const ld pi = atan2(0, -1);
const ld eps = 1e-4;
vector<vector<int>> sl;
vector<int> nearest;
int count(int v, int dist, int p = -1){
bool children = true;
int s = 0;
for(int u: sl[v]){
if(u == p) continue;
int c = count(u, dist + 1, v);
if(c < 0) children = false;
nearest[v] = min(nearest[v], nearest[u] + 1);
s += c;
}
if(nearest[v] <= dist) return 1;
if(s == 0 || !children) return -1;
return s;
}
void solve() {
int n, k;
cin >> n >> k;
sl.assign(n, vector<int>(0));
nearest.assign(n, n);
for(int i = 0; i < k; ++i){
int x;
cin >> x;
--x;
nearest[x] = 0;
}
for(int i = 1; i < n; ++i){
int u, v;
cin >> u >> v;
--u, --v;
sl[u].emplace_back(v);
sl[v].emplace_back(u);
}
cout << count(0, 0);
}
bool multi = true;
signed main() {
//freopen("in.txt", "r", stdin);
//freopen("in.txt", "w", stdout);
/*ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);*/
int t = 1;
if (multi) {
cin >> t;
}
for (; t != 0; --t) {
solve();
cout << "\n";
}
return 0;
}
Идея: Gol_D, MikeMirzayanov
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i, n) for (int i = 0; i < int(n); i++)
vector<ll> t, a;
ll s, tl;
const ll MAX = 1'000'000'000'000'000LL;
void build(int v, int l, int r) {
if (l == r)
t[v] = a[l];
else {
int m = (l + r) / 2;
build (v * 2, l, m);
build (v * 2 + 1, m + 1, r);
t[v] = min(t[v * 2], t[v * 2 + 1]);
}
}
void update(int v, int l, int r) {
if (l == r)
t[v] = MAX;
else {
int m = (l + r) / 2;
if (tl <= m)
update(v * 2, l, m);
else
update(v * 2 + 1, m + 1, r);
t[v] = min(t[v * 2], t[v * 2 + 1]);
}
}
int lower_bound_s(int v, int l, int r) {
if (r < tl || (l == r && s < -t[v])) {
return -1;
}
if (l == r || -t[v] <= s) {
return r;
}
int m = (l + r) / 2;
if (m < tl) {
return lower_bound_s(2 * v + 1, m + 1, r);
}
if (s < -t[2 * v]) {
return lower_bound_s(2 * v, l, m);
}
int res = lower_bound_s(2 * v + 1, m + 1, r);
return (res == -1) ? m : res;
}
int main() {
int tests;
cin >> tests;
forn(tt, tests) {
int n;
cin >> n >> s;
t = vector<ll>(4 * n);
a = vector<ll>(n);
forn(i, n) {
cin >> a[i];
}
for (int i = 1; i < n; ++i) {
a[i] += a[i - 1];
}
build(1, 0, n - 1);
int first = -1, second = -2;
for (tl = 0; tl < n; ++tl) {
int v = lower_bound_s(1, 0, n - 1);
if (v != -1 && v - tl > second - first) {
first = tl + 1;
second = v + 1;
}
s -= a[tl];
if (tl != 0) s += a[tl - 1];
update(1, 0, n - 1);
}
if (first == -1) {
cout << -1;
} else {
cout << first << " " << second;
}
cout << endl;
}
}
Идея: MikeMirzayanov
Разбор
Tutorial is loading...
Решение
#include <bits/stdc++.h>
using namespace std;
#define forn(i, n) for (int i = 0; i < int(n); i++)
#define sz(v) (int)v.size()
const int N = 1e6 + 50;
string a[N];
int n,m;
int ans;
void solve(int sum0) {
vector<int> v;
for (int sum = sum0, ad = 0, pref = 0; sum < n + m; sum += 2, ad++) {
vector<int> cur;
int li = max(0, sum - m + 1), ri = min(n - 1, sum);
if (li > ri) continue;
for (int i = li; i <= ri; i++) {
int j = sum - i;
if (a[i][j] == '1')
cur.emplace_back(i);
}
while (pref != sz(v) && v[pref] + ad > ri) {
pref++;
}
for (int i = pref; i < sz(v); i++) {
int new_val = v[i];
while (!cur.empty() && cur.back() - ad >= v[i]) {
new_val = max(new_val, cur.back() - ad);
cur.pop_back();
}
v[i] = new_val;
}
if (!cur.empty()) {
v.emplace_back(cur.back() - ad);
ans++;
}
}
}
int main() {
int t;
cin >> t;
forn(tt, t) {
cin >> n >> m;
forn(i, n) {
string s;
cin >> a[i];
}
ans = 0;
solve(0);
solve(1);
cout << ans << '\n';
}
}
Автокомментарий: текст был обновлен пользователем Gol_D (предыдущая версия, новая версия, сравнить).
By the way, in F many of you have written a two-pointer solution and I liked it. If anyone wants to write a proof, please.
Correct me if I am wrong
The problem is similar to problems where size of sliding window changes depending on some condition
Here the condition is we should always have enough money to perform next transaction.
In this explanation assume that we are always maintaining a $$$sum$$$ variable for the current amount of money in the ATM (initially $$$s$$$) and here $$$l$$$ signifies left pointer (initially 0) and $$$r$$$ right pointer (initially 0)
There are four changes possible
moving left pointer when it is a credit operation ===> subtract that value from $$$sum$$$ i.e. $$$sum-=a[l]$$$
moving left pointer when it is a withdrawal operation ===> add that value to $$$sum$$$ i.e. $$$sum+=a[l]$$$
moving right pointer when it is a withdrawal operation ===> check if sum is greater than required amount, if possible then subtract that value from $$$sum$$$ otherwise move left pointer ahead i.e.
if(sum >= a[r]) $$$sum-=a[r],r++$$$
else do $$$l++$$$ until we have enough money
moving right pointer when it is a credit operation ===> add that value to $$$sum$$$ as there is no pre-requisite to perform this operation i.e. $$$sum-=a[l]$$$
0-based indexing
Lets suppose we start from $$$0$$$ and we are able to perform all transactions upto $$$i$$$
Now obviously, the transaction at point $$$i+1$$$ has to be of withdrawal as we are always able to perform credit operation.
So , at $$$i+1$$$ we are unable to provide the required amount as ATM doesn't have that much
So we will rollback our previous operation upto a smallest (nearer to 0) point where we have enough money to perform the given operation
Now the $$$0th$$$ operation can be
====> If we remove this operation than obviously the amount of money inside the ATM will increase and in that case all the intermediate operations will have more than required money to perform operations so no intermediate operations is invalid.
====> If we remove this operations than obviously the amount we are having right now will decrease so it's not helpful to only remove this individual operation. As it doesn't help us to get enough money to proceed. So we will have to move our left pointer more ahead.
====> So it is safe to say that our rollbacks will always stop after a withdrawal operation because otherwise the amount of money will be less than previously we had at the point $$$i$$$ and in that way now operation in between will be invalid.
So if we are unable to have enough amount of money even if we rollback all previous operations then our left pointer will be $$$i+2$$$
Otherwise we will increment the right pointer by $$$1$$$.
So we can just keep doing this until the right pointer reaches the end of array and while extending our right pointer we will keep track of the max number of operations($$$[l,r]$$$) performed for answer.
This is first time I am writing proof of some problem.
So, sorry for my poor English or incomplete explanation (if found anywhere please comment below that part or a testcase where my explanation seems inappropriate)
Let us assume the max possible index(MPI) for some index L is R. Now for L+1, there are 2 possibilities:
I didn't realize 2 pointers solution worked, so I wrote segment tree + binary search. 2 pointers is much cleaner :)
how would you solve it if the problem were to find largest subsequence instead of contiguous subsequence ??
Several observations:
If we fix the leftmost position of the contiguous subsequence we chose to employ, we can binary search on our rightmost position. This is because choosing to help one more person on the right is always more expensive than not choosing to help that person.
How do we know if we can help a contiguous subsequence $$$[a_l, a_{l + 1} \dots a_{r - 1}, a_r]$$$? We can only help if $$$a_l, a_l + a_{l + 1}, a_l + a_{l + 1} + a_{l + 2} \dots a_l + a_{l + 1} + \dots + a_r$$$ are all positive. That is, if all the prefix sums are positive, or alternatively that our minimum prefix sum is $$$\ge 0$$$.
How do we quickly calculate minimum prefix sums which have some left offset? Just notice that prefix sums starting at $$$a_l$$$ are exactly the same as prefix sums starting at $$$a_0$$$, except we subtract off $$$pref[l - 1]$$$.
So in short, we can find the minimum prefix from $$$a_l \dots a_r$$$ by computing prefix sums $$$a_0 \dots a_r$$$, finding the minimum on the interval $$$[l, r]$$$, and subtracting off $$$pref[l - 1]$$$.
You can check out my submission here: 136938974 if you are still confused. Also, there's no need to cheese in this problem, but if you did need to cheese, you could be sparse table instead of segment tree for minimum, since the prefix sum array is static.
yeah i understood this one, but i asked a different question..i asked if we could solve this problem in O(NlogN) when we are asked to find longest subsequence instead of longest contiguous subsequence
I assume what you meant is creating the longest subsequence by removing some elements in the original sequence.
In this case, we can try to solve the problem greedily.
First we construct a minheap.
Then we will iterate through the original sequence using a pointer i.
If the ith element is negative, put it in the heap.
When the sum of all elements from 1 to i becomes negative, we will remove the element with the smallest value using the minheap we just constructed. Since the removed elements will have the smallest possible value, I think the resulting sequence will be the longest possible sequence.
I couldn't prove/disprove this idea, but it seems to work.
In China we call this solution "Monotone queue", maybe you can get some information by searching this word.
let's say p[i] is a prefix sum from L to i, by definition every i for p[i] must be greater or equal to -s
suppose we want to add a[R+1] to our subsequence
1.if p[R] + a[R+1] < -s, we must move our L pointer, it means we will remove some p from L to R let's say it p[j], then (p[R] + a[R+1]) — p[j] must be greater or equal to -s.
(p[R] + a[R+1]) — p[j] >= -s
(p[R] + a[R+1]) + s >= p[j] -> means p[j] < 0
what will happen with other p where j < i <= R when we removing p[j], by definition every p >= -s, since we are removing p[j] and p[j] < 0 and p[j] >= -s, then every p where j < i <= R will lose p[j],so it means any p from our new subsequence won't be less than -k
2.if p[R] + a[R+1] >= -s we can just move our R pointer to R + 1
solution: here
Alternative solution for G:
We have to cover the candies with chains. According to Dilworth's theorem it is enough to find the longest antichain. (set of candies, where it is impossible to collect two of them in the same turn).
Two candy $$$a$$$, $$$b$$$, (with the same parity) can be in the antichain if their width differnce is strictly larger than their height difference ($$$|y[a]-y[b]|>|x[a]-x[b]|$$$)
This motivates a dp solution $$$dp[i][j]$$$ — the longest anticahin from the first column ends $$$(i, j)$$$. The transitions are easy: $$$dp[i][j]$$$=max($$$dp[i-1][j-1]$$$, $$$dp[i+1][j-1]$$$) and $$$dp[i][j]$$$=$$$dp[i][j-2]+1$$$ (if there is a candy in (i, j).
The time and memory complexity is $$$O(n*m)$$$.
That was what I did in the contest. I was just a bit dumb and didn't realize that I could've just done a bit of dp. Instead, I solved with LIS, so the code got much longer than needed. Well, the final code was really similar to Baltic OI — 2009 — Candy.
Did anyone else solve E1 using LCA?
I solved E1 using LCA and a bruteforce-ish approach. For every leaves, I brute through all the enemy vertices in the list and then used LCA to calculate the distance from the leaf to the enemy and from the leaf to the root in $$$O(logN)$$$. I did a little optimization that if the leaf is an enemy vertex, I skip it.
In the worst case the complexity should be something around $$$O(N * k * logN)$$$ with $$$k$$$ is the number of enemies, since it is possible to build a tree with $$$N - 1$$$ leaves. Still not sure why my code didn't got FST, probabilistic magic maybe?
I did simple dfs two times. That is a O(n), check my submission here
https://codeforces.net/contest/1611/submission/136941781
Can you show me how did you do LCA with $$$O(logN)$$$?
my simple two pointer solution for problem F:
solution for F
Thanks for this, it was helpful.
thanks,dude,it's helpful!
Great
Please, help me. Why my solution for F get WA: https://codeforces.net/contest/1611/submission/136894757 ?
UPD: fixed: https://codeforces.net/contest/1611/submission/137155268
1
2 0
-1 5
correct output: 2 2
your output : 1 2
Thank you!
Can someone help me,please https://codeforces.net/contest/1611/submission/137113575
this test
get wrong answer distant to 8 is 7, distant to 5 is 8, but 5 earlier then 8 it's solution don't work because you don't check distant from root of tree.
Is this a bug? The editorials to the problems aren't linked in "Contest Materials" yet, only the Announcement is linked.
I think, my solution for F is easier than editorial's.
Time complexity: O (n * log ^ 2 (n))
Link: 137119559
P.S. you can use sparse table instead of segment tree.
You can use sliding window which is O(N)
how do we decide which one to slide?
use a prefix sum (or just a sum), try taking next value at j. if S + range_sum(i, j) < 0, then its not possible to take elements up to j starting at i, then move i until it is possible to do so. At this point, you then increment j and repeat the process.
something lile this:
using this idea, you know that every i already tested cannot excede the current j, (because at some point S + range_sum was less then 0). At the meantime, we are removing all i that is not usefull (doesn't get to j).
I think that the last question is: "Given an invalid window (i, j), what if I test an new_i (new_i > i) that can get to j, but there is some point x (new_i <= x < j) such that S + range_sum(new_i, x) < 0??", that is not possible because, initially our current window was invalid and:
if we remove a positive prefix of our window (i, j), then S + range_sum(new_i, j) is also invalid (less then an invalid range). That is the case explained before.
if we remove a negative prefix of our window (i, j), as S + range_sum(new_i, x) < 0, adding the removed negative prefix + S + range_sum(new_i, x) is also less then 0. Which means that S + range(i, x) is negative, therefore, j shoud never be greater then x, as j is the first invalid point. This is a contradiction.
thanks, i succesfully implemented this with a sum and two pointers
I think it should come like "Consider a vertex p[i], (2≤i≤n)" instead of "(2≤p[i]≤n)" in the editorial for D.
I did same as it written in the editorial.
By running a breadth-first search at the same time from each vertex with a friend, we can find the shortest distance to any friend from each vertex and by running from the root — the distance to the root. Now let's just go through all the leaves and check if there is one among them that the distance to the root is less.
Here is my implementation
What I did in my implementation is that
First I found the distance of every vertex from the root and stored it in the vector distance_from_root.
second, I found the minimum distance of every vertex from every friend, What I mean is that if the distance of a vertex x is 1,2,3 from friend x1,x2,x3 respectively then I stored the minimum of it i.e., 1 for vertex x in the vector distance_from_friend and then I checked for this condition as it is given int the editorial. Thus, Vlad can win if there is such a leaf (which, by condition, exits) for which the distance to the root is less than the distance to any of the friends.
But it is showing TLE. Please Help!
You run bfs from each friend, it's O(n*k). In the editorial, It was meant to run one bfs which uses all friends as start vertices like it is in solution.
I tried to solve "1611E1 — Escape The Maze (easy version)" using approach of traversing from friends up (parents) and from root (Vlad) down (to leaves) and at the same time. When friends visit some node I mark it as visited and that prevents Vlad to traverse through that node down. This approach gives me incorrect solution and I don't know why. Any ideas? Submission: https://codeforces.net/contest/1611/submission/136924541
Hi, I know its a long time ago but did you find the mistake in your solution?
My code 208031894 is giving wa on a test case too using the same approach.
Take a look at Ticket 16861 from CF Stress for a counter example.
Can someone explain to me my own approach for C xD? During the contest this seemed natural to me, but now I cannot convince myself why this actually works. In short, I compare leftmost and rightmost elements of
a
and whichever is smaller goes to the corresponding side inp
(ifa[left]
is smaller,a[left]
goes to beginning ofp
, otherwisea[right]
goes to end ofp
)Link
Can someone explain solution of E1?
Here is my solution for E1: https://codeforces.net/contest/1611/submission/137730474 I did not use the color thing, even though I can understand it now.
For my solution, I used a queue, a distance array, and simply made 2 BFS. The first BFS helps to know the distance between any vertex and Vlad's friends. This is feasible by putting all the friends' nodes as the sources (you put them in the queue and put the distance as 0). Why is that useful? Because we want to know whether a friend will be able to catch Vlad on a certain node. This is the case if the distance from the node to Vlad is bigger or equal to the distance from the node to the closest friend. Now that we have this distance array, we simply make a BFS with Vlad as the source, and verify that the distance from Vlad to each node is strictly smaller than the distances previously computed. If we end up on a node verifying this condition and this node appears to be a leaf, we know Vlad can escape without being caught!
I hope my explanation is clear, I had also put some comments in the code to make it understandable.
P.S.: for the color solution, it is basically the same but more optimized: we simulate both DFS at the same time. Vlad and his friends are the sources. Every node visited is colored with Vlad's color or his friends' color, IF it was not already colored. An important detail here is that we do not want Vlad to end up on a node that is at the same distance from one of his friends. To take this into account, we put Vlad's friends in the queue before him, so that they get to move before he does (color of friends has a higher priority than Vlad's color).
Let me know if something is not clear!
Can anyone spot the mistake in my code .. I am getting a runtime verdict on this code https://codeforces.net/contest/1611/submission/137548112
Thanx in advance
Can someone explain me the editorialist's Solution for G or anyother solution . It's been 1 day me trying to understand that . I did understood the solution provided by peti1234 in comments section .
I can solve problem G in O(n*m) by find path for each robot.
I divide robot into 2 parts, the same as MikeMirzayanov's solution.
With each part, while cell 1 exist, i go from top to bottom, with each row:
Get the first cell is 1, let's say p[i].
If robot can go to this cell, add i to list Q.
Else if column that robot exist smaller than this cell, skip this row, that mean p[i] < p[Q.back()].
Else, remove Q.back() from list, until robot can go to this cell.
After, make all cell in list to 0.
If we save p[i], p[i] always increase, and the loop is not overate m times, so complexity is O(n*m).
P/s: Sorry for my bad english.
Multi-Source BFS is amazing
In problem F,as per the equation in editorial,for each l in prefix array there would be value for right index in prefix array, given by p[r]<p[l-1]-s,so cant this this be implemented with a method of finding next greater element,with a little modification?
Solved problem F using binary search + segment tree.
But two pointer approach is nice and efficient.
I was Wondering why this came out of nowhere up in my feed. Ya F Was Great Problem Indeed.
I solved Problem F using binary Search + Segment Tree and then another approach I solved using two pointers can anyone explain editorial solution. It is going out of my head