CF Round 764 Div3 problem solution

Posted by AwptiK on Thu, 13 Jan 2022 17:01:22 +0100

Question A Plus One on the Subset (sign in)

There are \ (T(1\leq T \leq 10^4) \) groups of data.

Given an array \ (\ {a_n \} \) with a length of \ (n \), you can perform multiple operations. In each operation, you can add 1 to the value of any element. How many operations are required at least to make the values of all numbers in the array the same?

\(1\leq n \leq 50, 1\leq a_i\leq 10^9\)

Obviously, the answer is the maximum minus the minimum of the array.

#include<bits/stdc++.h>
using namespace std;
const int N = 60;
int n, a[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    return a[n] - a[1];
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

Question B Make AP (enumeration)

There are \ (T(1\leq T \leq 10^4) \) groups of data.

Given three numbers \ (a,b,c \), ask if you can multiply one of them by a positive integer \ (m \) so that the three numbers form an equal difference sequence in the original order?

\(1\leq a,b,c\leq 10^8\)

Obviously, the enumeration can multiply which number:

  1. Multiply \ (a \), then see if \ (2b-c \) is a multiple of \ (a \).
  2. Multiply \ (c \), similarly, see if \ (2b-a \) is a multiple of \ (c \).
  3. Multiply \ (b \), first see if \ (\ frac{a+c}{2} \) is an integer, and then see if it is a multiple of \ (b \).

Among them, the multiple must be divided to ensure that it must be a positive integer.

#include<bits/stdc++.h>
using namespace std;
const int N = 60;
int n, a[N];
bool can(int x, int y) {
	return x % y == 0 && x / y > 0;
}
bool solve() {
    int a, b, c;
	cin >> a >> b >> c;
	if (can(2 * b - a, c)) return true;
	if ((a + c) % 2 == 0 && can((a + c) / 2, b)) return true;
	if (can(2 * b - c, a)) return true;
	return false;
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << (solve() ? "YES" : "NO") << endl;
    return 0;
}

Question C Division by Two and Permutation

There are \ (T(1\leq T \leq 10^4) \) groups of data.

Now there are \ (n \) numbers, which are recorded as \ (a_1,a_2,\cdots,a_n \).

Now we can divide a number \ (x \) by 2 (round down). Can we operate these numbers several times to make them an arrangement from 1 to \ (n \)?

\(1\leq n \leq 50, 1\leq a_i\leq 10^9\)

Method 1: bipartite graph matching

During the game, the defense was directly broken, and the direct bipartite graph matched (escaped)

If a number can enter the interval \ ([1,n] \) during operation, then establish an edge from the original number to the new number (for example, 22, continuous operation can get 11, 5, 2, 1, 0, then we connect the point of 11 with 5, 2, 1).

After the graph is established, run the bipartite graph matching once. The expected complexity of the Hungarian algorithm is \ (O(Tn^2\log n) \) (the expected number of edges is \ (n\log n \).

#include<bits/stdc++.h>
using namespace std;
const int N = 60, M = 600;
int n, a[N];
//
int tot = 0, ver[M], Head[N], Next[M];
void init() {
	tot = 0;
	memset(ver, 0, sizeof(ver));
	memset(Head, 0, sizeof(Head));
	memset(Next, 0, sizeof(Next));
}
void addEdge(int u, int v) {
    ver[++tot] = v;
    Next[tot] = Head[u], Head[u] = tot;
}
//
int vis[N], match[N];
bool dfs(int u) {
    for (int i = Head[u]; i; i = Next[i]) {
        int v = ver[i];
        if (!vis[v]) {
            vis[v] = 1;
            if (!match[v] || dfs(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

bool solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    //build
    init();
    for (int i = 1; i <= n; ++i) {
        int x = a[i];
        while (x) {
            if (x <= n) addEdge(i, x);
            x /= 2;
		}
	}
	//run
	int ans = 0;
    memset(match, 0, sizeof(match));
    for (int i = 1; i <= n; ++i) {
        memset(vis, 0, sizeof(vis));
        ans += dfs(i);
    }
    return ans == n;
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << (solve() ? "YES" : "NO") << endl;
    return 0;
}

Method 2: greed

Let's build the map according to the above method, but don't run bipartite graph matching. After all, it's Div3's C problem. There must be some properties that can be simplified.

It is found that there is a transitive relationship between numbers (for example, 25 and 12, 12 can be transformed into numbers in the interval, and 25 can certainly), forming a relationship similar to topological order.

Then, according to greed, we can guess two conclusions:

  1. For numbers between intervals [1,n], you should fill in the large number first and then the decimal number (because there are not many numbers that can meet the large number)
  2. For a certain number \ (x \), if more than one number is satisfied, the smaller one (the larger one) is preferred to try to fill in other numbers

It's not very good to prove the correctness, but anyway, A has A complexity \ (O(Tn^2) \). (I don't know why. In fact, the speed of the two programs is about the same, both 900+ms. obviously, matching requires multiple logarithmic complexity)

#include<bits/stdc++.h>
using namespace std;
const int N = 60;
int n, a[N];
//
int vis[N];
vector<int> e[N];
void init() {
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < N; ++i) e[i].clear();
}
bool solve() {
    init();

    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    //build
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; ++i) {
        int x = a[i];
        while (x) {
            if (x <= n) e[x].push_back(i);
            x /= 2;
        }
    }
    //solve
    for (int i = n; i >= 1; i--) {
        bool flag = false;
        for (int j : e[i])
            if (!vis[j]) {
                vis[j] = 1;
                flag = true;
                break;
            }
        if (!flag) return false;
    }
    return true;
}
int main()
{
    int T;
    cin >> T;
    while (T--) cout << (solve() ? "YES" : "NO") << endl;
    return 0;
}

Question D Palindromes Coloring (two points, greedy)

There are \ (T(1\leq T \leq 10^4) \) groups of data.

Give a lowercase string \ (s \) with a length of \ (n \).

Now there are \ (k \) colors. You can choose to paint one color for each character (or not, but make sure that each color is selected by at least one character). Then, for each color, you will bring out all the characters of this color and should be able to arrange them into a palindrome string.

In terms of feasibility, it is not difficult (each color only corresponds to one character), so we want to find another thing: make the shortest length in \ (k \) palindrome strings the longest, and find the longest and shortest length.

\(1\leq k \leq n \leq 2*10^5\)

The minimum value is the maximum. It's not difficult to think of two points (escape)

Obviously, the internal order of the string has nothing to do with the answer, so we directly count how many characters there are in each character.

Analyzing the palindrome string, you will find that it has these two properties (actually one):

  1. There is only one character at most, and its occurrence times are odd
  2. For other characters, their occurrences are even

Then, let's simplify the above statistics directly and divide them into small segments with length of 1 and 2 (for example, bxyaxzay, we can split it into aa, xx, yy, b, z, that is, three small segments with length of 2 and two small segments with length of 1) It is found that this has no effect on the final answer, and can greatly reduce the complexity of state representation.

Then it's the favorite two minutes. For the length of check \ (x \):

  1. If \ (x=2t \), it is meaningless to add a small segment with length of 1. Just check whether the small segment with length of 2 is enough (whether there are \ (tk \))
  2. If \ (x=2t+1 \), try to piece together \ (k \) strings with a length of \ (2t \), then break up the small segments with a length of 2 and put them into the small segments with a length of 1 to see if the quantity is enough to spell these semi strings (whether there are \ (k \)

To sum up, the total complexity is \ (O(Tn) \).

(there is still a small hole in this problem. For example, the multiplication of t and k explodes int, which can be handled by long long or limiting the dichotomy range

#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, k;
char s[N];
//
int vis[26];
int a1, a2;
int check(int x) {
	int t = x / 2;
	if (t * k > a1) return false;
	if (x % 2 == 0) return true;
	else {
		int A1 = a1, A2 = a2;
		A1 -= k * t, A2 += 2 * A1;
		return A2 >= k;
	}
}
int solve() {
    scanf("%d%d", &n, &k);
    scanf("%s", s + 1);
    //solve
    a1 = a2 = 0;
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n; ++i)
    	vis[s[i] - 'a']++;
	for (int i = 0; i < 26; ++i) {
		int x = vis[i];
		int x2 = x % 2, x1 = (x - x2) / 2;
		a1 += x1, a2 += x2;
	}
    int l = 0, r = n / k + 1;
    while (l < r) {
    	int mid = (l + r + 1) >> 1;
    	if (check(mid)) l = mid;
    	else r = mid - 1;
	}
	return l;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--) printf("%d\n", solve());
    return 0;
}

Question E Masha-forgetful (DP, string processing, simulation)

The title is complicated and difficult to translate. Farewell

We divide the string to be simplified into several strings, and each string can find the corresponding segment in the previous phone numbers.

Obviously, the shorter the string, the better (this is obvious). In addition, the topic only requires the output of legal schemes, but there is no limit on the number of segments, then we must follow the shortest principle. Because the segment length is at least 2, we directly find 1100 strings with lengths of 2 and 3, and directly record their corresponding coordinates for each type.

Then, for the string, we directly start DP, and the equation is roughly as follows:

\[f_i=\begin{cases} f_{i-2}+v(i-1,i) \\ f_{i-3}+v(i-2,i)\end{cases} \]

\(v(l,r) \) refers to the coordinates of the string mapping on the string \ ([l,r] \), addition refers to the scheme pasted to the back, and any of the two formulas can be satisfied.

The idea is simple, but the result code is too numb. Let's make do with it (the estimated complexity is \ (O(nm\log_2^{1100}) \), that is, \ (O(nm) \) comes with super huge constants. After all, a pile of STL S is very slow

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n, m;
char book[N][N], s[N];
struct Node {
    int l, r, id;
    void print() {
        printf("%d %d %d\n", l, r, id);
    }
};
map<string, Node> vis;
string getStr(char *str, int l, int r) {
    string res = "";
    for (int i = l; i <= r; ++i)
        res += str[i];
    return res;
}

Node dp[N];
int Last[N];
int calc(int x) {
    if (x == 0) return 0;
    return calc(Last[x]) + 1;
}
void print_ans(int x) {
    if (x == 0) return;
    print_ans(Last[x]);
    dp[x].print();
}
void solve()
{
    //read
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%s", book[i] + 1);
    scanf("%s", s + 1);
    //init
    vis.clear();
    for (int i = 1; i <= n; ++i)
        for (int j = 2; j <= m; ++j)
            vis[getStr(book[i], j - 1, j)] = (Node){j - 1, j, i};
    for (int i = 1; i <= n; ++i)
        for (int j = 3; j <= m; ++j)
            vis[getStr(book[i], j - 2, j)] = (Node){j - 2, j, i};
    //solve
    memset(Last, -1, sizeof(int) * (m + 1));
    Last[0] = 0;
    for (int i = 1; i <= m; ++i) {
        if (i >= 2 && Last[i - 2] != -1 && vis.find(getStr(s, i - 1, i)) != vis.end()) {
            dp[i] = vis[getStr(s, i - 1, i)];
            Last[i] = i - 2;
        }
        else if (i >= 3 && Last[i - 3] != -1 && vis.find(getStr(s, i - 2, i)) != vis.end()) {
            dp[i] = vis[getStr(s, i - 2, i)];
            Last[i] = i - 3;
        }
    }
    //
    if (Last[m] == -1) puts("-1");
    else {
        printf("%d\n", calc(m));
        print_ans(m);
    }
}
int main()
{
	int T;
    scanf("%d", &T);
    while (T--) solve();
    return 0;
}

Question F Interacdive Problem (dichotomy, interaction)

Now give a positive integer \ (n \).

Now we know that there is a number \ (x \), we do not know its specific value, but only know that its range is in the interval \ ([1,n) \).

However, we can interact with the computer to get some information about this number:

\(+ \; c \): add \ (c \) to the number \ (x \), and then return the value of \ (\ lfloor\frac{x}{n}\rfloor \)

Now we hope that we can do no more than 10 operations to find the current value of \ (x \) (the value after several operations) and output it to the computer (the specific format follows the topic specification, which will not be repeated here)

\(2<n\leq 1000,1\leq x,c < n\)

Classic dichotomy + interaction, very annoying.

We look at the first-hand examples and find that we can set two variables \ (L,R \) as the upper and lower bounds of the possible values of \ (x \), and then continuously shorten the range by asking until the value of \ (x \) is determined. Obviously, the initial condition is \ (L=1,R=n-1 \) and it happens to be \ (\ log_2^{1000} \) for 10 times. The idea is obvious: each query must reduce the upper and lower bounds to half of the original range until the value of \ (x \) is completely obtained.

I don't know how to describe this thing in words, but the core steps are the following:

  1. Add a number \ (delta \) to both the left and right endpoints \ (L,R \) and $x $so that the mid of the interval becomes a multiple of \ (n \)
  2. After adding \ (delta \), we get the value of \ (\ lfloor\frac{x}{n}\rfloor \) at this time. We make it \ (t \), then we get the new area \ ([tn,(t+1)n-1] \). We notice that the left and right endpoints of this interval are multiples of \ (n \), and the middle value of \ ([L,R] \) is also a multiple of \ (n \), which means that as long as you take the intersection of two intervals, you can reduce the length of the interval by half
  3. Repeat the above steps until \ (L=R \) (at most \ (\ log_2^{1000}=10 \) times, which is exactly the limit given for the topic), that is, the \ (x \) required to be output
#include<bits/stdc++.h>
using namespace std;
int n;
void add(int v) {
    printf("+ %d\n", v);
    fflush(stdout);
}
int query() {
    int x;
    scanf("%d", &x);
    return x;
}
int main()
{
    scanf("%d", &n);
    int L = 1, R = n - 1;
    while (L < R) {
        int mid = (L + R + 1) >> 1;
        int delta = (mid / n + 1) * n - mid;
        L += delta, R += delta;
        add(delta);

        int t = query();
        int L1 = t * n, R1 = (t + 1) * n - 1;
        if (L <= L1 && L1 <= R) L = L1;
        else R = R1;
    }
    printf("! %d", L);
    fflush(stdout);
    return 0;
}

Question G MinOr Tree (binary technique, figure Unicom)

There are \ (T(1\leq T \leq 10^4) \) groups of data.

Given an undirected weighted graph with \ (n \) points \ (m \) edges, we are required to select (or retain only) \ (n-1 \) edges from it, so that the or sum of edge weights (\ (w_1 \operatorname{or} w_2 \operatorname{or}\cdots \operatorname{or} w_{n-1} \) is minimized and output it.

\(3\leq n \leq 2*10^5,n -1\leq m \leq 2*10^5,1\leq w_i\leq 10^9 \), no self ring

According to the principle of binary representation, we consider it from high to low (considering \ (10 ^ 9 < 2 ^ {30} \), so we only consider 30 bits)

For example, for the highest bit (30 bits), we first consider whether we can try to select some edges so that after \ (\ operatorname{or} \), this bit is 0 (that is, select all edges, and they are 0 above this bit) and these edges are enough to make the graph connected. If so, keep these edges for the next round, otherwise keep all edges. In this way, the minimum value can be obtained by repeating 30 times.

In terms of complexity, each round of judgment can use parallel search (\ (O(m\log{n}) \)) or deep search (\ (O(n+m) \)), and then judge a total of 30 rounds.

 #include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, m;
struct Edge {
    int u, v, w;
};
vector<Edge> e1, e2;
//
int fa[N];
int find(int x) {
    if (x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}
bool check() {
    for (int i = 1; i <= n; ++i)
        fa[i] = i;
    int cnt = n;
    for (Edge e : e2) {
        int u = e.u, v = e.v;
        u = find(u), v = find(v);
        if (u != v) fa[u] = v, cnt--;
    }
    return cnt == 1;
}
int solve()
{
    //read & init
    scanf("%d%d", &n, &m);
    e1.clear();
    e2.clear();
    for (int i = 0; i < m; ++i) {
        Edge e;
        scanf("%d%d%d", &e.u, &e.v, &e.w);
        e1.push_back(e);
    }
    //solve
    int res = 0;
    for (int dig = 29; dig >= 0; dig--) {
        for (Edge e : e1)
            if (((e.w >> dig) & 1) == 0) e2.push_back(e);
        if (check()) swap(e1, e2);
        else res |= 1 << dig;
        e2.clear();
    }
    return res;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--) printf("%d\n", solve());
    return 0;
}

Topics: acm