2024.11.27 週三

move_quiet發表於2024-11-27

2024.11.27 週三

  • Q1. 1000
    給定x,y,設ai=i^x,bi=i^y,問兩個無窮序列a和b的最長公共子序列的長度。

  • Q2. 1400
    給定一條鏈,所有點未啟用,在開始點放一棋子並啟用,2人輪流操作:將棋子移動到相鄰的未啟用的點並啟用。不能操作者輸。問均最優策略的贏家。

  • Q3. 1600
    給你兩個大小為n×m的矩陣a,b,其中元素的是n×m的排列,你可以任意進行行變換/列變換,問是否透過操作使得矩陣a變成矩陣b。

  • A1. 補:怎麼感覺今天的題1000分的最難 >_<
    神秘位運算題 "看到本題的資料範圍,可以猜想這是一道結論題,結合樣例就出了。"
    [l,r]⊕x=[l′,r′]⊕y => [l,r]=[l′,r′]⊕(x⊕y)。令v=(x⊕y),即求最長的 [l,r]使得異或上v後依然連續。
    如果 v=abc100000,任意 l=cde100000,r=cde111111一定是最長的,答案為lowbit(v)。

  • A2. 補:我就說對不上樣例得"仔細"重讀題吧,讀漏了。
    顯然只有第一步操作有2種選擇,其餘操作沒有選擇,且奇數為必勝態。直接搜尋兩側鏈的點數,若有奇數便可勝。

  • A3. 13mins-33mins 假思路:最初的想法是判斷每一行的和和每一列的和,但是不嚴謹。行和只是必要條件,非充分。後又想到用行首元素vector存其餘元素,然後發現判斷困難。
    發現無論怎麼交換同一行和同一列中的元素種類是不會改變的,只會改變相對位置。 因此答案就是判斷a中每一行和每一列在b中是否有無序版。
    判斷方式多樣如雜湊,我這裡直接使用並查集把a每一行看成一個集合,在b中判斷是否衝突,列同理。

A1.

#include <bits/stdc++.h>
#define int long long //
#define endl '\n'     //
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
const int mod = 998244353;
const int N = 10 + 5e5;
void _();
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
        _();
    return 0;
}

//  給定x,y,設ai=i^x,bi=i^y,問兩個無窮序列a和b的最長公共子序列的長度。

//  神秘位運算題 "看到本題的資料範圍,可以猜想這是一道結論題,結合樣例就出了。"
//  [l,r]⊕x=[l′,r′]⊕y => [l,r]=[l′,r′]⊕(x⊕y)。令v=(x⊕y),即求最長的 [l,r]使得異或上v後依然連續。
//  如果 v=abc100000,任意 l=cde100000,r=cde111111一定是最長的,答案為lowbit(v)。
void _()
{
    int x, y;
    cin >> x >> y;
    auto lowbit = [](int x)
    {
        return x & -x;
    };
    cout << lowbit(x ^ y) << endl;
}

A2.

#include <bits/stdc++.h>
// #define int long long //
#define endl '\n' // 互動/除錯 關
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
void _();
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T--)
        _();
    return 0;
}

//  給定一條鏈,所有點未啟用,在開始點放一棋子並啟用,2人輪流操作:將棋子移動到相鄰的未啟用的點並啟用。不能操作者輸。問均最優策略的贏家。
//  我就說對不上樣例得"仔細"重讀題吧,讀漏了。
//  顯然只有第一步操作有2種選擇,其餘操作沒有選擇,且奇數為必勝態。直接搜尋兩側鏈的點數,若有奇數便可勝。
void _()
{
    int n, t;
    cin >> n >> t;
    vector<vector<int>> e(n + 1);
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    int st = 0;
    cin >> st;
    vector<int> vis(n + 1);
    function<int(int)> dfs = [&](int u)
    {
        if (vis[u])
            return 0;
        vis[u] = 1;
        int cnt = 1;
        for (auto v : e[u])
            cnt += dfs(v);
        return cnt;
    };
    vis[st] = 1;
    bool f = 0;
    for (auto u : e[st])
        if (dfs(u) & 1)
            f = 1;
    cout << (f ? "Ron" : "Hermione") << endl;
}

A3.

#include <bits/stdc++.h>
#define int long long //
#define endl '\n'     // 互動/除錯 關
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
void _();
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--)
        _();
    return 0;
}

//  給你兩個大小為n×m的矩陣a,b,其中元素的是n×m的排列,你可以任意進行行變換/列變換,問是否透過操作使得矩陣a變成矩陣b。

//  假思路:最初的想法是判斷每一行的和和每一列的和,但是不嚴謹。行和只是必要條件,非充分。後又想到用行首元素vector存其餘元素,然後發現判斷困難。
//  發現無論怎麼交換同一行和同一列中的元素種類是不會改變的,只會改變相對位置。 因此答案就是判斷a中每一行和每一列在b中是否有無序版。
//  判斷方式多樣如雜湊,我這裡直接使用並查集把a每一行看成一個集合,在b中判斷是否衝突,列同理。
//  13mins-33mins

// 帶權並查集
vector<int> p, vs, es; // 集合數 點數 邊數 (對一個連通塊而言)
void init(int n1)      // p[x]不一定為根節點  find(x)一定是根節點
{
    int n = n1 + 2;
    p.assign(n, 0);
    vs.assign(n, 0);
    es.assign(n, 0);
    for (int i = 1; i <= n1; i++)
        p[i] = i, vs[i] = 1, es[i] = 0;
}
int find(int x) // 找到根節點
{
    if (p[x] == x)
        return x;
    int px = find(p[x]);
    return p[x] = px;
}
bool same(int a, int b)
{
    return find(a) == find(b);
}
void merge(int a, int b) // 合併集合
{
    int pa = find(a);
    int pb = find(b);
    if (pa == pb) // pa pb 均為根節點 p[pa]==pa
    {
        es[pa]++; // 1個集合 邊+1
        return;
    }
    p[pb] = p[pa];        // 改變b的根節點
    vs[pa] += vs[pb];     // 將b合併進a
    es[pa] += es[pb] + 1; // 2個集合
}
int size(int a) //  集合內的元素的個數
{
    return vs[find(a)];
}
//  init(n);

void _()
{
    int n, m;
    cin >> n >> m;
    init(n * m);
    vector<vector<int>> a(n + 1, vector<int>(m + 1)), b(n + 1, vector<int>(m + 1));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
            if (j - 1)
                merge(a[i][j], a[i][1]);
        }

    bool f = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> b[i][j];
            if (j - 1 && !same(b[i][j], b[i][1]))
                f = 0;
        }
    }
    init(n * m);
    for (int j = 1; j <= m; j++)
        for (int i = 1; i <= n; i++)
            if (i - 1)
                merge(a[i][j], a[1][j]);
    for (int j = 1; j <= m; j++)
        for (int i = 1; i <= n; i++)
            if (i - 1 && !same(b[i][j], b[1][j]))
                f = 0;
    cout << (f ? "YES" : "NO") << endl;
}
// void _()
// {
//     int n, m;
//     cin >> n >> m;
//     vector<vector<int>> a(n + 1, vector<int>(m + 1)), b(n + 1, vector<int>(m + 1));
//     map<int, vector<int>> a_row, b_row;
//     for (int i = 1; i <= n; i++)
//     {
//         int x;
//         for (int j = 1; j <= m; j++)
//         {
//             cin >> a[i][j];
//             if (j == 1)
//                 x = a[i][j];
//             a_row[x].push_back(a[i][j]);
//         }
//     }
//     for (int i = 1; i <= n; i++)
//     {
//         int x;
//         for (int j = 1; j <= m; j++)
//         {
//             cin >> b[i][j];
//             if (j == 1)
//                 x = b[i][j];
//             b_row[x].push_back(b[i][j]);
//         }
//     }
//     bool f = 1;
//     for (auto &[x, a] : a_row)
//     {
//         auto &b = b_row[x];
//         sort(a.begin(), a.end());
//         sort(b.begin(), b.end());
//         if (a != b)
//             f = 0;
//     }
//     cout << (f ? "YES" : "NO") << endl;
// }
// void _()
// {
//     int n, m;
//     cin >> n >> m;
//     vector<vector<int>> a(n + 1, vector<int>(m + 1)), b(n + 1, vector<int>(m + 1));
//     map<int, int> a_row, b_row, a_col, b_col;
//     for (int i = 1; i <= n; i++)
//     {
//         int s = 0;
//         for (int j = 1; j <= m; j++)
//         {
//             cin >> a[i][j];
//             s += a[i][j];
//         }
//         a_row[s]++;
//     }
//     for (int i = 1; i <= n; i++)
//     {
//         int s = 0;
//         for (int j = 1; j <= m; j++)
//         {
//             cin >> b[i][j];
//             s += b[i][j];
//         }
//         b_row[s]++;
//     }
//     for (int j = 1; j <= m; j++)
//     {
//         int s = 0;
//         for (int i = 1; i <= n; i++)
//             s += a[i][j];
//         a_col[s]++;
//     }

//     for (int j = 1; j <= m; j++)
//     {
//         int s = 0;
//         for (int i = 1; i <= n; i++)
//             s += b[i][j];
//         b_col[s]++;
//     }

//     int f = 1;
//     for (auto [s, cnt] : a_row)
//         if (cnt - b_row[s])
//             f = 0;
//     for (auto [s, cnt] : a_col)
//         if (cnt - b_col[s])
//             f = 0;
//     cout << (f ? "YES" : "NO") << endl;
// }