Codeforces round #900 (Div.3) Editorial
- Get link
- X
- Other Apps
Codeforces round #900 (Div.3) Editorial
Thank you for participating in the first ever Serbian Div. 3 Codeforces round!
Before the editorial, we are sorry for the big gap between problems C and D.
This was the first round we ever created (except for wxhtzdy).
We are striving to become better problem-setters and create more balanced problems-sets in the future.
Also a massive thanks to Vladosiya for giving us a chance to create a divison 3 round!
1878A - How Much Does Daytona Cost?
Author: wxhtzdy
Prepared by: ognjentesic, AndrewPav, AlphaMale06
When is the answer definitely "NO"?
What happens if there is no element equal to
What happens if there is an element equal to
1878A - How Much Does Daytona Cost?
It's enough to check if there even exists the element equals to
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; //read the number of test cases
cin >> t;
while(t--){
int n, k;
cin >> n >> k; //read n and k
bool ys=0; //bool value if true then there exists a subsegment which satisfies the condition, else it doesn't exist
for(int i=0; i< n; i++){
int a; //read the i-th element of the array
cin >> a;
if(a==k)ys=1; //if the element is equal to k, then the subsegment [i, i] has the most common element equal to k
}
if(ys)cout << "YES\n"; //output the answer
else cout << "NO\n";
}
}
Author: ognjentesic
Prepared by: ognjentesic, AlphaMale06
Watch parity.
What does the parity of
What parity are numbers which divide odd numbers?
1878B - Aleksa and Stack
There are many solutions to this problem, but the intended one is the following:
By selecting the first
Time complexity:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; //read the number of test cases
cin >> t;
while(t--){
int n; //read n
cin >> n;
for(int i=0; i< n; i++)cout << i*2+1 << " "; //write the first n odd numbers in order
cout << '\n';
}
}
Author: AlphaMale06
Prepared by: ognjentesic, AlphaMale06
Prove that the answer for the second test case is "NO".
Determine the minimum and maximum sum achievable.
Prove that it's possible to construct the desired sequence for any requested sum between the minimum and maximum possible sum.
1878C - Vasilije in Cacak
It is clear that the minimum sum is obtained for the numbers
Furthermore, it is evident that the maximum sum is achieved for the numbers
Let's prove that among any
So, among any
Therefore, by applying the principle of mathematical induction, we can obtain any sum that is greater than or equal to minumum sum and less than or equal to maximum sum.
#include <iostream>
using namespace std;
int main(){
int t; //read the number of test cases
cin >> t;
while(t--){
long long n, x, k; //read n, x, k for each test case
cin >> n >> x >> k;
if(2*k>=x*(x+1) && 2*k<=n*(n+1)-(n-x)*(n-x+1)){ //check if k is between the minimum and maximum sum
cout << "YES\n";
}
else cout << "NO\n";
}
}
Author: AlphaMale06
Prepared by: ognjentesic, AlphaMale06
For each
What happens when we make the same modification twice?
Does the order of the operations matter?
Try pre-processing the queries
1878D - Reverse Madness
Observation 1: if we look at [
Now, without loss of generality, because of the first observation, we can consider the same problem but with
It is easy to see that the modifications
Now try visualizing the modifications:
if
if
if
We can logically conclude that the modifications are symmetrical with respect to the middle of the string.
From this symmetry, we can conclude that if a modification "touches" index
This means that the order of modifications doesn't matter, because for each index it only matters how many modifications affect it.
Another thing to note is that for a given index
Let's store the number of modifications for each index in an array, and if
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; //number of test cases
cin >> t;
while(t--){
int n, k; //read the input
cin >> n >> k;
string s;
cin >> s;
int a[k]; int b[k];
for(int i=0; i< k; i++){cin >> a[i]; a[i]--;}
for(int i=0; i< k; i++){cin >> b[i]; b[i]--;}
int q;
cin >> q;
int cnt[n+1]={0}; //read and preprocess queries
for(int i=0; i< q; i++){
int x; cin >> x; cnt[x-1]++;
}
string ans="";
for(int i=0; i<k; i++){ //treat each interval as a seperate test case
string s1=s.substr(a[i], b[i]-a[i]+1);
int sum=0;
int l=a[i];
int r=b[i];
for(int j=l; j<=(l+r)/2; j++){
sum+=cnt[j]+cnt[r-j+l];
if(sum&1)swap(s1[j-l], s1[r-j]);
}
ans+=s1;
}
cout << ans << '\n';
}
}
Try calculating
Which condition has to hold true for all elements on a subsegment
How can we check if that condition is true for a certain bit fast?
Try prefix sums.
Try binary search.
1878E - Iva & Pav
We can, for each bit, calculate the prefix sums of the array (
Next, for each query, we can use binary search to find
It is possible to optimize the solution even more by using sparse tables, to calculate
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N =200003;
const int bits=30;
int pref[N][bits];
int a[N];
void Buildprefix(int n){ //Builds the prefix sums for each bit
for(int i=0; i< n; i++){
for(int j=0; j<30; j++){
if(a[i]&(1<<j)){
pref[i+1][j]=pref[i][j]+1;
}
else{
pref[i+1][j]=pref[i][j];
}
}
}
}
void solve(){
int n;
cin >> n;
for(int i=0; i< n; i++){
cin >> a[i];
}
Buildprefix(n);
int q;
cin >> q;
while(q--){
int l, k;
cin >> l >> k;
if(a[l-1]<k){
cout << -1 << '\n';
continue;
}
int lo=l;
int hi=n;
int ans=l;
while(lo<=hi){
int s=(lo+hi)/2;
int num=0;
for(int j=0; j< bits; j++){
if(pref[s][j]-pref[l-1][j]==s-l+1){
num+=(1<<j);
}
}
if(num>=k){
lo=s+1;
ans=max(ans, s);
}
else hi=s-1;
}
cout << ans << '\n';
}
}
int main(){
int t = 1;
cin >> t;
while(t--){
solve();
}
}
1878F - Vasilije Loves Number Theory
Author: ognjentesic, AlphaMale06
Prepared by: ognjentesic, AlphaMale06
What does the condition
How does any allowed operation affect
Try to prove that the answer is "YES" if
Try to prove that the answer is "NO" otherwise.
How can you find and maintain the number of divisors quickly?
To make the implementation easier, try using the condition
1878F - Vasilije Loves Number Theory
Answer: a solution exists if and only if
Proof: consider the prime factorization of the number
Let's now consider the operations we are performing. We are multiplying the number
Multiplying
Using this fact, we just need to efficiently check whether
First we pre-calculate for each positive integer less than
Now we need to deal with the queries:
For type 2 query we just need to reset everything.
For the first query, we factorize
Solution 1:
We do this by multiplying the value of all previous type
Time complexity
Solution 2:
This solution is even faster, but it might be harder to implement, and that's why we didn't make it necessary.
Instead of storing queries, we just need to use another map to store the prime divisors of
#include <bits/stdc++.h>
using ll = long long;
using namespace std;
const int N = 1000003;
map<int, int> powers; //key is a prime number, value is te highest powert
map<int, int> original_divisors; //same as map powers but isn't updated during queries, it's used to reset the powers
int smallest_divisor[N]; //precalculated smallest divisors to make factorization O(logx)
bool mark[N]; //marks numbers which aren't prime (used for sieve)
ll divisor_count = 1; //the number of divisors (updated during queries)
void prime(){ //calculates the smallers divisor for each number from 1 to N (sieve of erathosetenes)
smallest_divisor[1]=1;
smallest_divisor[2]=2;
for(int i=4; i<N; i+=2){
mark[i]=true;
smallest_divisor[i]=2;
}
for(int i=3; i<N; i+=2){
if(!mark[i]){
smallest_divisor[i]=i;
for(ll j = i*1ll*i; j<N; j+=2*i){
if(!mark[j]){
smallest_divisor[j]=i;
mark[j]=1;
}
}
}
}
}
//a function for factorizing a number (used to process queries and factorize n in the beginning)
//it also updates the highest prime which divides n (powers map), and the number of divisors of n (divisor_count)
void factorize(int x){
int p=0;
int current_divisor = 1;
while(x>1){ //while x has non 1 divisors, divide it by it's smallest divisor which isn't 1(smallers divisor if always prime)
if(smallest_divisor[x]!=current_divisor){
if(p>0){
divisor_count/=powers[current_divisor]+1;
powers[current_divisor]+=p;
divisor_count*=powers[current_divisor]+1;
}
p=1;
current_divisor=smallest_divisor[x];
}
else{
p++;
}
x/=smallest_divisor[x];
}
if(p>0){
divisor_count/=powers[current_divisor]+1;
powers[current_divisor]+=p;
divisor_count*=powers[current_divisor]+1;
}
return;
}
int main(){
prime(); //precalculate smallest divisors
int t;
cin >> t;
while(t--){
//read n and q
int n; int q;
cin >> n >> q;
//factorize n
factorize(n);
//since factorize updates the powers map, update the origional_divisors map too
for(auto prime : powers){
original_divisors[prime.first]=prime.second;
}
int original_divisor_count = divisor_count; //since factorize updates the divisor_count we update the original_divisor_count too
vector<int> queries; //storing previous queries
//processing queries
while(q--){
int query_type;
cin >> query_type;
if(query_type==1){ //query of type 1 (multiply n by x)
int x;
cin >> x;
factorize(x); //factorize x, update the powers map, and the number of divisors
queries.push_back(x); //add x to the list of previous queries
ll num=n;
for(int query : queries){ //check if the product of all previous queries and n is divisible by d(n)
num*=query;
num%=divisor_count;
}
if(num==0){ //if it is the answer is yes else the answer is no
cout << "YES\n";
}
else cout << "NO\n";
}
else{ //here we should reset everything related to the type 1 query
powers.clear(); //clear the powers map and set it to original divisors and powers
for(auto original_div : original_divisors){
powers[original_div.first]=original_div.second;
}
divisor_count=original_divisor_count; //restart the divisor_count
queries.clear(); //clear the queries (since we only need the queries since the previous type 2 query)
}
}
original_divisors.clear();
powers.clear();
divisor_count=1;
original_divisor_count =1;
if(t) cout << "\n";
}
}
Author: wxhtzdy
Prepared by: ognjentesic, OAleksa, AlphaMale06
How many vertices actually matter on the path from
How do we find them?
Root the tree arbitrarily.
Try calculating the number of occurrences for each bit on the path from the root to a certain vertex.
Try binary search.
Try LCA (Lowest common ancestor).
1878G - wxhtzdy ORO Tree
Observation 1: it's enough to only consider
Now we need to find those vertices. We know that for each bit, the vertex which we will consider can give this bit to
How do we find these vertices qucikly? First let's arbitrarily root the tree. Now let's precalculate for each vertex and bit
We do binary search by taking
Time complexity:
#include <bits/stdc++.h>
#define f first
#define s second
using namespace std;
#define int long long
const int maxn = 2e5 + 69;
const int k = 19;
const int bits = 30;
vector<int> g[maxn];
int n, q, a[maxn], up[maxn][k], tin[maxn], tout[maxn], timer, d[maxn];
int r[maxn][k];
int bst[maxn][bits];
void dfs(int v, int p, vector<int> b) {
tin[v] = ++timer;
up[v][0] = p;
r[v][0] = a[p];
d[v] = d[p] + 1;
for (int i = 0;i < bits;i++) {
bst[v][i] = b[i];
if (a[v] & (1 << i))
b[i] = v;
}
for (int i = 1;i < k;i++) {
up[v][i] = up[up[v][i - 1]][i - 1];
r[v][i] = r[v][i - 1] | r[up[v][i - 1]][i - 1];
}
for (auto u : g[v]) {
if (u != p)
dfs(u, v, b);
}
tout[v] = timer;
}
bool is_anc(int u, int v) {
return tin[u] <= tin[v] && tout[u] >= tout[v];
}
int lca(int u, int v) {
if(is_anc(u, v))
return u;
else if(is_anc(v, u))
return v;
for (int i = k - 1;i >= 0;i--) {
if (!is_anc(up[u][i], v) && up[u][i] > 0)
u = up[u][i];
}
return up[u][0];
}
int OR(int u, int dis) {
int res = a[u];
for (int j = 0;j < bits;j++) {
if (dis & (1 << j)) {
res |= r[u][j];
u = up[u][j];
}
}
return res;
}
int Qry(int u, int v) {
int lc = lca(u, v);
return OR(u, d[u] - d[lc]) | OR(v, d[v] - d[lc]);
}
signed main()
{
int tt = 1;
cin >> tt;
while(tt--) {
cin >> n;
timer = 0;
for (int i = 1;i <= n;i++)
g[i].clear();
for (int i = 1;i <= n;i++)
cin >> a[i];
for (int i = 1;i <= n - 1;i++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
vector<int> temp(30, -1);
dfs(1, 0, temp);
cin >> q;
for (int i = 1;i <= q;i++) {
int x, y;
cin >> x >> y;
int LCA = lca(x, y);
vector<int> t;
t.push_back(x);
t.push_back(y);
for (int i = 0;i < bits;i++) {
if (bst[x][i] != -1 && is_anc(LCA, bst[x][i]))
t.push_back(bst[x][i]);
if (bst[y][i] != -1 && is_anc(LCA, bst[y][i]))
t.push_back(bst[y][i]);
}
int ans = __builtin_popcount(a[x]) + __builtin_popcount(a[y]);
for (auto p : t) {
int x1 = a[x], x2 = a[y];
x1 |= Qry(x, p);
x2 |= Qry(y, p);
ans = max(ans, 1ll * __builtin_popcount(x1) + __builtin_popcount(x2));
}
cout << ans << " ";
}
cout << "\n";
}
return 0;
}
- Get link
- X
- Other Apps
Comments
Post a Comment