Explanation of the 2021 Zhejiang University of technology freshman competition

Posted by a-mo on Thu, 09 Dec 2021 13:46:57 +0100

  • The order of questions in this article is the order of expected difficulty, not the order of competition questions
  • All "better optimization" in this article are thoughts beyond the standard answer. You can pass the question without using this content

Game expectations

Total number of participants: 175 (the number of participants who have passed at least one question will not be included in the total number of participants who have not passed the question)

TitleActual number of passesActual passing ratioExpected passing ratio
chiking's idol"EDIT""EDIT"100%
chiking and cordoli"EDIT""EDIT"80%
chiking sequence II"EDIT""EDIT"60%
chiking's sequence I"EDIT""EDIT"50%
chiking's chessboard"EDIT""EDIT"30%
Helpful chiking"EDIT""EDIT"20%
chiking's Tetris"EDIT""EDIT"1%
chiking, let's do the check-in question"EDIT""EDIT"1%
chiking is a robot"EDIT""EDIT"1%

P.S. "EDIT" is too lazy to count. Let's make do with it first

Problem solution

chiking's idol

General meaning

Loop output \ soup_god /, and the total output string length is n n n

thinking

A simple check-in question can be written as long as you remember the operation of mod

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
    const char *data = "\\soup_god/";
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cout << data[i % 10];
    }
    cout << endl;
}

chiking and cordoli

General meaning

n n n items. Each item has a value and category for you to choose n − k n - k n − k categories of items, so that the total value of the selected items in these categories is the largest

thinking

It is also one of the check-in questions

When reading in the data, count the value of each category, and then sort it, and take it out n − k n - k The value of n − k categories is sufficient

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
    int w[11] = {0};
    int m, n, k;
    cin >> m >> n >> k;
    for (int i = 0; i < m; ++i) {
        int d, s;
        cin >> d >> s;
        w[s] += d;
    }
    sort(w + 1, w + n + 1);
    int ans = 0;
    for (int i = k + 1; i <= n; ++i) ans += w[i];
    cout << ans << endl;
}

Make complaints

This question originally had a small pit, that is, the value of the item can be negative, so it needs to add an additional judgment, but after thinking about it, it's still a check-in question. Don't be disgusted deliberately, so it's deleted. So by the way, how many people see the problem and wonder if the value can be negative

chiking sequence II

General meaning

There is a non decreasing array that allows you to perform any number of operations, and each operation can increase one of the values 1 1 1. How many operations are required to make the array have no same value

thinking

Because it can only be added, the number will only increase. Because the array has been sorted, the simplest solution is to make each value larger than the previous value

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> data(n);
    for (int i = 0; i < n; ++i) cin >> data[i];
    long long sum = 0;
    for (int i = 1; i < n; ++i) {
        int tmp = max(data[i - 1] + 1, data[i]);
        sum += tmp - data[i];
        data[i] = tmp;
    }
    cout << sum << endl;
}

chiking's sequence I

General meaning

There is an array that allows you to perform any operation. Each operation can insert any number into the array to any position. Ask how many operations are required to satisfy each value in the array a i ≤ i a_i \leq i ai ≤ i, where i i i indicates subscript

thinking

First, make the number of each bit less than or equal to its subscript, so the inserted number is no exception. Since the inserted number can be any value, it is natural to choose 1 1 1 insertion is the best choice, because no matter where it is inserted, it can make the newly inserted number no longer need to be considered

Next, consider the insertion position in the equation a i a_i ai , is a non updatable value, so we can only find a way to make it i i If i increases, the easiest solution is to insert the number at the beginning of the array, which will increase the subscripts of all the original values in the array and meet the conditions to the greatest extent

Next, consider the number of inserts. Since they are all inserted at the front of the array, you can rewrite the equation as a i < = i + x a_i <= i + x ai < = I + X, where x x x is the value to be solved. So for each i i i have to satisfy this equation, so traverse the array once to find the most needed x x x is enough

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
    int _;
    cin >> _;
    for (int ts = 0; ts < _; ++ts) {
        int n;
        cin >> n;
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            int tmp;
            cin >> tmp;
            ans = max(ans, tmp - i - 1);
        }
        cout << ans << endl;
    }
}

chiking's chessboard

General meaning

use k k Can k L-shaped squares be tiled into a rectangular square

thinking

We can use L-shaped squares to spell the following two simplest tiling schemes

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-jhybmf9b-1639041859538) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 2 * 4. PNG)]

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-q0hqwnc4-1639041859539) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 3 * 8. PNG)]

Both options are 8 8 Multiple of 8 squares, so at least the total number of squares should be 8 8 Multiple of 8, i.e n × m = 8 k n \times m = 8k n×m=8k

  1. If both sides are even, then one side must be 2 2 2, and the other side is 4 4 4, so it must be tiled only by the first tiling scheme
  2. If one side is odd and the other side is even, then one side must be 8 8 8, and the first scheme can also be rewritten as 2 ∗ 8 2 * 8 2 * 8 square, that is, only the other side can be decomposed into 2 x + 3 y 2x + 3y The form of 2x+3y can be obtained easily as long as the value of $\ geq 2 $

So the conclusion: as long as the number of squares is 8 8 8, and both sides are $\ geq 2 $, it must be tiled

Next, you just need to calculate whether the quantity is right or not

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
#define int long long
    int _;
    cin >> _;
    for (int ts = 0; ts < _; ++ts) {
        int n, m, k;
        cin >> n >> m >> k;
        if (n > 1 && m > 1 && (n * m) % 8 == 0) {
            int cnt = (n * m / 4);
            if (cnt == k) cout << "Perfect!\n";
            else cout << "Single forever!\n";
        } else cout << "Single forever!\n";
    }
}

Helpful chiking

General meaning

On the premise that oo and u in the string are allowed to be converted to each other, and kh and h of the string are allowed to be converted to each other, there are several different strings in the calculated string array

thinking

Because there is conversion, the best way is to convert to one type and compare

For the oo and u rules, if we convert all oo to u, when we encounter ou and uo, we will find that the strings that should be equal under this rule are not equal. So all u characters should be converted to oo

kh and h, if we convert all h to kh, then kh can continue to be converted to kkh, kkkh, k kkkkh, etc., so we can only choose to convert kh to H. However, please note that kkkh such continuous k can be converted several times continuously

After processing, count the number of different strings

Processing string complexity O ( n m ) = 1 e 5 O(nm) = 1e5 O(nm)=1e5
Count the number of different strings O ( n 2 m ) = 1 e 7 O(n^2m) = 1e7 O(n2m)=1e7

Meet requirements

AC Code

#include "bits/stdc++.h"

using namespace std;

char a[1010], b[2010];

void solve() {
    int n;
    cin >> n;
    vector<string> data;
    for (int i = 0; i < n; ++i) {
        cin >> a;
        int len = (int) strlen(a);
        int pos = 0;
        for (int j = 0; j < len; ++j) {
            if (a[j] == 'u') {
                b[pos++] = 'o';
                b[pos++] = 'o';
            } else if (a[j] == 'h') {
                while (pos > 0 && b[pos - 1] == 'k') pos--;
                b[pos++] = 'h';
            } else {
                b[pos++] = a[j];
            }
            b[pos] = 0;
        }
        data.emplace_back(b);
    }
    int cnt = 0;
    for (int i = 0; i < n - 1; ++i) {
        bool flag = true;
        for (int j = i + 1; j < n; ++j) {
            if (data[i] == data[j]) {
                flag = false;
                break;
            }
        }
        if (flag) cnt++;
    }
    cout << cnt + 1 << endl;
}

Better optimization

In fact, what needs to be compared here is whether the strings are the same, so you can use string hash to solve it. In this way, the complexity will be reduced to O ( n m ) O(nm) O(nm). Of course, there are some hidden dangers of possible errors in string hash, but it can be solved by adding multiple groups of hash

Better Code

#include "bits/stdc++.h"

using namespace std;

char a[1010], b[2010];

void solve() {
    int n;
    cin >> n;
    set<long long> hashCode;
    for (int i = 0; i < n; ++i) {
        cin >> a;
        int len = (int) strlen(a);
        int pos = 0;
        for (int j = 0; j < len; ++j) {
            if (a[j] == 'u') {
                b[pos++] = 'o';
                b[pos++] = 'o';
            } else if (a[j] == 'h') {
                while (pos > 0 && b[pos - 1] == 'k') pos--;
                b[pos++] = 'h';
            } else {
                b[pos++] = a[j];
            }
        }
        long long code = 0;
        const long long p = 131, m = 1e9+7;
        for (int j = 0; j < pos; ++j) {
            code *= p;
            code %= m;
            code += b[j];
            code %= m;
        }
        hashCode.insert(code);
    }
    cout << hashCode.size() << endl;
}

chiking's Tetris

General meaning

The Tetris game with a width of 4 has only two kinds of diamonds: 2 × 2 2 \times 2 two × 2 blocks and 1 × 4 1 \times 4 one × 4, find the optimal score when all the falling orders are known

thinking

There are many things to think about. Let's prove some things first and use them later

In fact, there are only 10 and 3 elimination blocks

This conclusion should be relatively simple, because there are only squares with lengths of 2 and 4 in the vertical direction, so when we can create 10 points, we will not lose as soon as possible

Long squares must be combined as 2 × 4 2 \times 4 2×4

This conclusion means that the following situations are impossible (the blue box with arrow is the last to fall)

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-bn9035ct-1639041859540) (/ image / ACM / 2021-zjgsu-acm-freshman competition / Tetris cannot be inserted. PNG)]

Because in this case, it will appear 1 × 4 1 \times 4 one × 4 cannot be inserted into the original 1 × 4 1 \times 4 one × Make into 2 × 4 2 \times 4 2×4

This situation just meets a 10 point elimination situation, as shown in the figure below

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-wxj8myeq-1639041859540) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 10-1. PNG)]

Since you can create 10 points, there is no need to worry about whether you can merge. First, it becomes 10 points important, so there is no single long square

Putting 2 * 2 in the middle must not be a good choice

This conclusion should be obvious, because placing left / right can be compared with 1 × 4 1 \times 4 one × 4 points can also be eliminated with 2 × 2 2 \times 2 two × 2. It must be better to eliminate points than to put them in the middle

The original structure does not affect the current expected score

This sentence expands to

Regardless of the impact of the previous blocks, if the next n blocks can create a value of 10 points, they can create a value of 10 points. If the next n blocks can create a value of 3 points, they can create a value of 3 points

This one will not be proved for the time being

All combinations are as follows

All the combinations that can get points are only the following, of which only the first two are 10 points and the others are 3 points

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-phbsoyvp-1639041859540) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 10-1. PNG)]

![10-2](/image/acm/2021-ZJGSU-ACM-freshman-competition/10-2.png

[external link picture transfer failed, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-yNdZwLFI-1639041859541)(/image/acm/2021-ZJGSU-ACM-freshman-competition/3-1.png)]

[external link picture transfer fails, and the source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-PAtpKcOk-1639041859541)(/image/acm/2021-ZJGSU-ACM-freshman-competition/3-2.png)]

Next, we can prove the above conclusion

The impact of the previous box is mainly due to the fact that it must fall first, resulting in the occupation of a certain position, so that the box that can be eliminated can not continue to be eliminated

One of the most common effects is 2 × 2 2 \times 2 two × 2. In short, there is already one piece 2 × 2 2 \times 2 two × 2 grid, at this time, it may not be able to meet the above elimination conditions, as shown in the figure below

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-aed0ha2s-1639041859541) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 2-2-block. PNG)]

For example, at this time, you can't perfectly meet the second 10 point elimination situation, because you can't make four points 1 × 4 1 \times 4 one × 4 in the same line, that is, four fall at the same time 1 × 4 1 \times 4 one × You can't get 10 points through these four squares alone

But what?

In fact, we can still get 10 points, and the final result is still 2 × 2 2 \times 2 two × 2 blocks, as shown in the figure below

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-YsV3O7nJ-1639041859541)(/image/acm/2021-ZJGSU-ACM-freshman-competition/2-2-block-solve.png)]

I won't introduce each situation in detail here for your consideration 2 × 2 2 \times 2 two × Under the influence of 2, can all four scoring schemes get the original score without new influence

Another effect is 2 × 4 2 \times 4 two × 4. It's similar. I won't explain it in detail

Special, whether in 2 × 2 2 \times 2 two × 2 or 2 × 4 2 \times 4 two × Under the influence of 4, the third scoring scheme may change from 3 points to 10 points. Although this increases the score, it also increases unpredictable problems, which must be solved. Interestingly, this new scoring scheme is actually the first scheme, so if we can give priority to calculating all possible first schemes, this situation is no longer possible, so it can be ignored

except 2 × 2 2 \times 2 two × 2 or 2 × 4 2 \times 4 two × 4. There are more possibilities, such as higher 2 × 8 2 \times 8 two × 8 and so on, but in fact it is similar, so there is no need to prove it

Of course, there are more outrageous effects, such as 1 × 4 1 \times 4 one × 4. Obviously, this influence really affects the score, because the fourth scoring scheme is impossible to score at all, as shown in the figure below

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-tpxsvovz-1639041859542) (/ image / ACM / 2021-zjgsu-acm-freshman competition / 1-4-block-solve. PNG)]

But pay attention to what the title says 1 × 4 1 \times 4 one × 4 must be even, so there must be one after it 1 × 4 1 \times 4 one × 4. Then we return to the conclusion at the beginning. Here we can get another 10 points

The interspersed combination has no effect

To put it simply, the insertion of two blocks required by the structure will not affect the final score, so I won't introduce it in detail here

When you have proved it, the next step is to simulate and discuss all the situations

Pay attention to the first 10 points, which requires that the last one to fall must be 1 × 4 1 \times 4 one × 4 long bar can be used, and the rest can be counted

AC Code

#include "bits/stdc++.h"

using namespace std;

void solve() {
    int n;
    cin >> n;
    int tot0 = 0, cnt0 = 0, cnt1 = 0, ans = 0;
    for (int i = 0; i < n; ++i) {
        int tmp;
        cin >> tmp;
        if (tmp == 1) {
            cnt1++;
            if (cnt1 >= 2 && cnt0 >= 2) {
                cnt1 -= 2;
                cnt0 -= 2;
                ans += 10;
            }
        } else {
            tot0++;
            cnt0++;
        }
    }
    ans += (cnt1 / 4) * 10;
    cnt1 &= 2;
    if (tot0 >= 2) {
        ans += (cnt0 + cnt1) / 2 * 3;
    } else if (cnt0 && cnt1) {
        ans += 3;
    }
    cout << ans << endl;
}

chiking, let's do the check-in question

General meaning

Given an N, it is allowed to use addition, subtraction, multiplication and division and any parentheses to find out how many different four numbers there are, so that these four numbers can calculate n, and there must be decimals in the operation process. List all possible combinations of four numbers

thinking

It's actually a question of violence

Calculate the complexity, 1 3 4 / 2 ∗ 4 ! ∗ 2 ∗ 3 6 = 499 , 703 , 256 13^4 / 2 * 4! * 2 * 3^6 = 499,703,256 134/2∗4!∗2∗36=499,703,256

Explain, list every possibility for each location → 1 3 4 / 2 \rightarrow 13^4 / 2 → 134 / 2 (avoid repetition and ensure that the previous value is not greater than the current value in the traversal process)

Arrange array → 4 ! \rightarrow 4! →4!

There are two types of parentheses ( ( A @ B ) @ C ) @ D ((A @ B) @ C) @ D (( A@B )@C) @ d and ( A @ B ) @ ( C @ D ) (A @ B) @ (C @ D) (A@B)@(C@D)( A B C D ABCD ABCD is a number, @ @ @Is an operation symbol), so → 2 \rightarrow 2 →2

Enumerate all operation symbols → 3 6 \rightarrow 3^6 → 36 (6 operations are A + B , A − B , B − A , A × B , A ÷ B , B ÷ A A + B, A - B, B - A, A \times B, A \div B, B \div A A+B,A−B,B−A,A×B,A÷B,B÷A)

It's easy to give up some schemes in advance by pruning, and the complexity can be reduced

So just direct violence

But how to elegant violence?

AC code

#include "bits/stdc++.h"

using namespace std;

const double eps = 1e-6;


void solve() {

    int m;
    set<int> ans;
    int curValue[4];
    cin >> m;

    auto isM = [&](double cur) { return abs(cur - m) < eps; };
    auto isDouble = [&](double cur) { return abs(cur - int(cur + eps)) > eps; };

    auto add = [](double a, double b) { return a + b; };
    auto sub1 = [](double a, double b) { return a - b; };
    auto sub2 = [](double a, double b) { return b - a; };
    auto times = [](double a, double b) { return a * b; };
    auto div1 = [](double a, double b) { return a / b; };
    auto div2 = [](double a, double b) { return b / a; };

    function<double(double, double)> arr[6] = {add, sub1, sub2, times, div1, div2};

    auto cal = [&]() {
        bool reach = false, hasNoDouble = false;
        do {

            function<bool(double, int, bool)> dfs1 = [&](double cur, int deep, bool hasDouble) {
                if (deep == 4) {
                    if (isM(cur)) {
                        reach = true;
                        if (!hasDouble) {
                            hasNoDouble = true;
                            return false;
                        }
                    }
                    return true;
                }

                return all_of(arr, arr + 6, [&](function<double(double, double)> &func) {
                    double nxt = func(cur, curValue[deep]);
                    return dfs1(nxt, deep + 1, hasDouble || isDouble(nxt));
                });
            };
            function<bool()> dfs2 = [&]() {
                for (auto &i: arr) {
                    for (auto &j: arr) {
                        double l = i(curValue[0], curValue[1]);
                        double r = j(curValue[2], curValue[3]);
                        for (auto &k: arr) {
                            double t = k(l, r);
                            if (isM(t)) {
                                reach = true;
                                if (!isDouble(l) && !isDouble(r)) {
                                    hasNoDouble = true;
                                    return false;
                                }
                            }
                        }
                    }
                }
                return true;
            };

            dfs1(curValue[0], 1, false);
            dfs2();

        } while (next_permutation(curValue, curValue + 4));

        if (reach && !hasNoDouble) {
            // Four int numbers less than 16 can be bit compressed into an int
            int t = 0;
            for (auto &item: curValue) {
                t <<= 4;
                t += item;
            }
            ans.insert(t);
        }
    };

    // It is not necessary to use dfs for violent enumeration. In fact, for can also be used, or even faster, because it reduces the time-consuming of entering and leaving the stack
    for (int i = 0; i < 13; ++i) {
        curValue[0] = i + 1;
        for (int j = i; j < 13; ++j) {
            curValue[1] = j + 1;
            for (int k = j; k < 13; ++k) {
                curValue[2] = k + 1;
                for (int l = k; l < 13; ++l) {
                    curValue[3] = l + 1;
                    cal();
                }
            }
        }
    }

    cout << ans.size() << endl;
    for (auto &item: ans) {
        int cur = item;
        for (int i = 3; i >= 0; --i) {
            curValue[i] = cur % 16;
            cur >>= 4;
        }
        for (int i = 0; i < 4; ++i)
            cout << curValue[i] << " \n"[i == 3];
    }
}

chiking is a robot

General meaning

There is a map with obstacles. There are three different machines. One can only go down, the other can only go right, and the last one can move down and right. Ask q times whether a machine can go from the starting point to the end point

thinking

Considering the first two machines, it is actually very easy to solve. Take the first machine as an example. I add the first line of the whole map to the second line and the second line to the third line. After this operation, each point saves how many walls are directly above the point, which is called "prefix wall". If the number of "prefix walls" of the starting and ending points is the same, you can reach it, otherwise there must be a wall in the middle

The second machine will not be introduced too much

The third kind of machine is more difficult to do. Consider the possibility of dp: if this point is not a wall, all points that can reach this point are the union of all points that can reach the point above this point and all points that can reach the left of this point. Describe it with a formula

KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ dp[i][j] =...

With this calculation, we can get the first code

#include "bits/stdc++.h"

using namespace std;

struct Node {
    int x1, y1, x2, y2, i;

    bool operator<(const Node &rhs) const {
        return x2 < rhs.x2;
    }
};

void solve() {
    const int N = 510;
    const int M = 510;

    // Read map
    int n, m;
    cin >> n >> m;
    vector<bitset<M>> graph(n);

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            char c;
            cin >> c;
            graph[i][j] = c == '0';
        }
    }

    // Prefix and solve 1 and 2 models
    vector<vector<int>> modelList[2];
    for (auto &model: modelList) {
        model.resize(n);
        for (auto &item: model)
            item.resize(m, 0);
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            modelList[0][i][j] = (i != 0 ? modelList[0][i - 1][j] : 0) + !graph[i][j];
            modelList[1][i][j] = (j != 0 ? modelList[1][i][j - 1] : 0) + !graph[i][j];
        }
    }

    int q;
    cin >> q;
    vector<bool> ans(q);    // Offline calculation answer
    vector<Node> query;
    query.reserve(q);

    for (int i = 0; i < q; ++i) {
        int t, x1, y1, x2, y2;
        cin >> t >> x1 >> y1 >> x2 >> y2;
        // Subscript starts at 0
        x1 -= 1;
        y1 -= 1;
        x2 -= 1;
        y2 -= 1;

        if (t == 1) ans[i] = (y1 == y2) and (x1 <= x2) and (modelList[0][x1][y1] == modelList[0][x2][y2]);
        else if (t == 2) ans[i] = (x1 == x2) and (y1 <= y2) and modelList[1][x1][y1] == modelList[1][x2][y2];
        else {
            if (x1 > x2 || y1 > y2) ans[i] = false;
            else query.push_back((Node) {x1, y1, x2, y2, i});
        }
    }

    sort(query.begin(), query.end());

    auto hashNode = [&](const int &x, const int &y) {
        return x * n + y;
    };

    vector<bitset<N * M>> dp(m);
    for (int i = 0; i < m; ++i) {
        dp[i].reset();
        dp[i][hashNode(0, i)] = graph[0][i];
    }

    int curQuery = 0;

    for (int i = 0; i < n; ++i) {
        // Calculate offline what can be reached at each location
        if (!graph[i][0]) dp[0].reset();
        else dp[0][hashNode(i, 0)] = true;
        for (int j = 1; j < m; ++j) {
            if (!graph[i][j]) dp[j].reset();
            else {
                dp[j][hashNode(i, j)] = true;
                dp[j] |= dp[j - 1];
            }
        }
        while (curQuery < query.size()) {
            if (query[curQuery].x2 == i) {
                ans[query[curQuery].i] = dp[query[curQuery].y2][hashNode(query[curQuery].x1, query[curQuery].y1)];
                curQuery++;
            } else break;
        }
    }

    for (int i = 0; i < q; ++i)
        cout << (ans[i] ? "yes" : "no") << endl;
}

Let's calculate the complexity: O ( n 4 ) = 50 0 4 = 62 , 500 , 000 , 000 O(n^4) = 500^4 = 62,500,000,000 O (N4) = 5004 = 62500000000, which is definitely not possible. Even the pressure level cannot pass

So it needs to be optimized

The problem lies in the need to calculate the reachability of all points, which leads to the need to calculate a node O ( n 2 ) O(n^2) The time of O(n2) is very unreasonable

If you try to reduce one n n n. Then we can only calculate whether we can reach the value of a row or a column, not all

But consider two-way, if we know the starting point, we can reach a node. If the target point can come from the same point, it can also be said that it can be reached

Therefore, this special row or column divides the map into two parts. At the same time, if you ask if it is across this row or column, you can answer, but how to solve the problem of not across?

Divide and conquer

Taking the transverse as an example, the transverse intermediate axis is continuously taken as a specific line, and the solution of the query across this axis is continuously calculated. The complexity is O ( n 3 l o g n ) = 50 0 3 ∗ l o g ( 500 ) = 337 , 371 , 250 O(n^3logn) = 500^3 * log(500) = 337,371,250 O(n3logn)=5003 * log (500) = 337371250, which seems feasible

You can get a second code

#include "bits/stdc++.h"

using namespace std;

const int QUERY_LEN = 1e5 + 10;
const int NUM = 505;
const int DIV = 505;

char mp[NUM][NUM];
int n, m, ans[QUERY_LEN];
int L[NUM][NUM], U[NUM][NUM];
struct node {
    int id, x1, y1, x2, y2;
};
vector<node> queryList;
unsigned vis1[NUM][NUM][DIV], vis2[NUM][NUM][DIV];

void reset(unsigned *v) {
    memset(v, 0, sizeof(unsigned) * DIV);
}

void cpFlag(unsigned *dist, const unsigned *from) {
    memcpy(dist, from, sizeof(unsigned) * DIV);
}

void orFlag(unsigned *dist, const unsigned *from) {
    for (int i = 0; i < DIV; ++i) dist[i] |= from[i];
}

void setFlagTrue(unsigned *v, int id) {
    v[id] = 1;
}

bool andFlagAny(const unsigned *a, const unsigned *b) {
    for (int i = 0; i < DIV; ++i) if (a[i] & b[i]) return true;
    return false;
}

void dfs(int l, int r, vector<node> &q) {
    if (l > r) return;
    int mid = (l + r) >> 1;
    for (int i = mid; i >= l; i--) {
        for (int j = m; j >= 1; j--) {
            reset(vis1[i][j]);
            if (mp[i][j] == '1') continue;
            if (i == mid) setFlagTrue(vis1[i][j], j);
            else cpFlag(vis1[i][j], vis1[i + 1][j]);
            orFlag(vis1[i][j], vis1[i][j + 1]);
        }
    }
    for (int i = mid; i <= r; i++) {
        for (int j = 1; j <= m; j++) {
            reset(vis2[i][j]);
            if (mp[i][j] == '1') continue;
            if (i == mid) setFlagTrue(vis2[i][j], j);
            else cpFlag(vis2[i][j], vis2[i - 1][j]);
            orFlag(vis2[i][j], vis2[i][j - 1]);
        }
    }
    vector<node> vl, vr;
    for (auto it: q) {
        if (it.x2 < mid) vl.push_back(it);
        else if (it.x1 > mid) vr.push_back(it);
        else ans[it.id] = andFlagAny(vis1[it.x1][it.y1], vis2[it.x2][it.y2]);
    }
    dfs(l, mid - 1, vl);
    dfs(mid + 1, r, vr);
}

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == '0') L[i][j] = L[i][j - 1] + 1;
        }
    }
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (mp[i][j] == '0') U[i][j] = U[i - 1][j] + 1;
        }
    }

    int q;
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int op, x1, x2, y1, y2;
        cin >> op >> x1 >> y1 >> x2 >> y2;
        if (x1 > x2 || y1 > y2) {
            ans[i] = 0;
            continue;
        }
        if (op == 1) {
            if (y1 != y2 || U[x2][y2] < x2 - x1) ans[i] = 0;
            else ans[i] = 1;
        } else if (op == 2) {
            if (x1 != x2 || L[x2][y2] < y2 - y1) ans[i] = 0;
            else ans[i] = 1;
        } else {
            queryList.push_back({i, x1, y1, x2, y2});
        }
    }
    dfs(1, n, queryList);

    for (int i = 1; i <= q; i++) cout << (ans[i] ? "yes\n" : "no\n");
}

However, it's still wrong. In fact, the space is out of limit

Think carefully. In fact, we use int to simulate a Boolean value group, which is a waste of space. We can press the bit to get the final code. At this time, the complexity is O ( n 3 l o g n / 64 ) = 50 0 3 ∗ l o g ( 500 ) / 64 = 5 , 271 , 425 O(n^3logn / 64) = 500^3 * log(500) / 64 = 5,271,425 O(n3logn/64)=5003∗log(500)/64=5,271,425,

#include "bits/stdc++.h"

using namespace std;

const int QUERY_LEN = 1e5 + 10;
const int NUM = 505;
const int DIV = 8;
const int LEN = 64;
typedef unsigned long long bits;

char mp[NUM][NUM];
int n, m, ans[QUERY_LEN];
int L[NUM][NUM], U[NUM][NUM];
struct node {
    int id, x1, y1, x2, y2;
};
vector<node> queryList;
bits vis1[NUM][NUM][DIV], vis2[NUM][NUM][DIV];

void reset(bits *v) {
    memset(v, 0, sizeof(bits) * DIV);
}

void cpFlag(bits *dist, const bits *from) {
    memcpy(dist, from, sizeof(bits) * DIV);
}

void orFlag(bits *dist, const bits *from) {
    for (int i = 0; i < DIV; ++i) dist[i] |= from[i];
}

void setFlagTrue(bits *v, int id) {
    v[id / LEN] |= ((bits) 1u) << (id % LEN);
}

bool andFlagAny(const bits *a, const bits *b) {
    for (int i = 0; i < DIV; ++i) if (a[i] & b[i]) return true;
    return false;
}

void dfs(int l, int r, vector<node> &q) {
    if (l > r) return;
    int mid = (l + r) >> 1;
    for (int i = mid; i >= l; i--) {
        for (int j = m; j >= 1; j--) {
            reset(vis1[i][j]);
            if (mp[i][j] == '1') continue;
            if (i == mid) setFlagTrue(vis1[i][j], j);
            else cpFlag(vis1[i][j], vis1[i + 1][j]);
            orFlag(vis1[i][j], vis1[i][j + 1]);
        }
    }
    for (int i = mid; i <= r; i++) {
        for (int j = 1; j <= m; j++) {
            reset(vis2[i][j]);
            if (mp[i][j] == '1') continue;
            if (i == mid) setFlagTrue(vis2[i][j], j);
            else cpFlag(vis2[i][j], vis2[i - 1][j]);
            orFlag(vis2[i][j], vis2[i][j - 1]);
        }
    }
    vector<node> vl, vr;
    for (auto it: q) {
        if (it.x2 < mid) vl.push_back(it);
        else if (it.x1 > mid) vr.push_back(it);
        else ans[it.id] = andFlagAny(vis1[it.x1][it.y1], vis2[it.x2][it.y2]);
    }
    dfs(l, mid - 1, vl);
    dfs(mid + 1, r, vr);
}

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == '0') L[i][j] = L[i][j - 1] + 1;
        }
    }
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (mp[i][j] == '0') U[i][j] = U[i - 1][j] + 1;
        }
    }

    int q;
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int op, x1, x2, y1, y2;
        cin >> op >> x1 >> y1 >> x2 >> y2;
        if (x1 > x2 || y1 > y2) {
            ans[i] = 0;
            continue;
        }
        if (op == 1) {
            if (y1 != y2 || U[x2][y2] < x2 - x1) ans[i] = 0;
            else ans[i] = 1;
        } else if (op == 2) {
            if (x1 != x2 || L[x2][y2] < y2 - y1) ans[i] = 0;
            else ans[i] = 1;
        } else {
            queryList.push_back({i, x1, y1, x2, y2});
        }
    }
    dfs(1, n, queryList);

    for (int i = 1; i <= q; i++) cout << (ans[i] ? "yes\n" : "no\n");
}

Of course, it would be better if you knew bitset

Topics: Algorithm acm