Hướng dẫn giải của Bedao Regular Contest 18 - Thành phố đông dân nhất


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Ta chia ~m~ đề xuất thành các block độ dài ~K = \sqrt M~.

Ta có vector truy vấn dưới dạng: vector<int> Q[N/K][N/K]

Nếu truy vấn có ~r-l+1 \le 2 \times K~, ta duyệt qua các cạnh và sử dụng ~DFS/BFS~ để tính thành phần liên thông có tổng lớn nhất.

Ngược lại, gọi ~X~ là chỉ số của block chứa ~l~, ~Y~ là chỉ số của block chứa ~r~, ta lưu truy vấn này vào ~Q[X+1][Y-1]~ để tra lời truy vấn sau.

Sau khi hoàn tất quá trình trên, ta sẽ xử lí các truy vấn chưa được tính. Ta sẽ duyệt như sau:

for (Y : N/K --> 1)
    Khởi tạo lại DSU
    for (X : Y --> 1)
        Thêm các cạnh của X vào DSU.
        Trả lời các truy vấn  trong Q[X][Y].
        Các truy vấn trên  tối đa 2*K cạnh chưa xét, lúc này xử  như trường hợp đầu tiên.
#include <bits/stdc++.h>
#define BIT(x, i) (((x)>>(i))&1)
#define MASK(i) (1LL<<(i))
#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define discrete(x) x.resize(unique(all(x)) - x.begin())
#define mp make_pair
#define pb push_back
#define TASK "test"

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<pii> vii;
typedef vector<ll> vll;
typedef vector<pll> vlll;

template<typename T1, typename T2> bool mini(T1 &a, T2 b) {if(a>b) a=b; else return 0; return 1;}
template<typename T1, typename T2> bool maxi(T1 &a, T2 b) {if(a<b) a=b; else return 0; return 1;}
template<typename T1> T1 abs(T1 a) {if(a<0) a*=-1; return a;}

mt19937_64 rd(chrono::steady_clock::now().time_since_epoch().count());
ll rand(ll l, ll h){return l + ll(rd()) * rd() %(h-l+1);}

const int BL = 470;
int blockIDof(int i) {return i/BL;}
pair<int, int> idsOfBlock(int i) {return {i*BL, (i+1)*BL-1};}

class DSU
{
public:
    DSU(int n, vector<int> a)
    : n(n)
    , a(a)
    , par(n, -1)
    , w(a)
    , ans(a[max_element(all(a)) - a.begin()])
    , st()
    {
        reset();
    }

    void reset() {
        while(!st.empty()) rollback();
    }

    int getAns() {
        return ans;
    }

    void join(int u, int v) {
        u = root(u);
        v = root(v);
        State state = {u, v, par[u], par[v], w[u], w[v], ans};
        st.push_back(state);
        if(u == v) return;
        if(par[u] > par[v]) swap(u, v);
        par[u] += par[v];
        par[v] = u;
        w[u] += w[v];
        w[v] = 0;
        maxi(ans, w[u]);
    }

    void rollback() {
        State state = st.back(); st.pop_back();
        par[state.u] = state.parU;
        par[state.v] = state.parV;
        w[state.u] = state.wU;
        w[state.v] = state.wV;
        ans = state.ans;
    }

private:
    int root(int u) {
        return par[u] < 0 ? u : root(par[u]);
    }

private:
    struct State
    {
        int u, v, parU, parV, wU, wV, ans;
    };

public:
    int n;
    vector<int> a, par, w;
    int ans;
    vector<State> st;
};

int n, m, q;
vector<int> a, x, y, l, r, ans;

int solveEdgeCase(DSU &dsu, int l, int r) {
    for(int i = l; i <= r; ++i)
        dsu.join(x[i], y[i]);
    auto ans = dsu.getAns();
    dsu.reset();
    return ans;
}

int solveCompressedCase(DSU &dsu, int l, int r) {
    int L = idsOfBlock(blockIDof(l)+1).fi;
    int R = idsOfBlock(blockIDof(r)-1).se;
    assert(L <= R);
    assert(l <= L && R <= r);

    for(int i = l; i < L; ++i)
        dsu.join(x[i], y[i]);
    for(int i = R + 1; i <= r; ++i)
        dsu.join(x[i], y[i]);

    int ans = dsu.getAns();

    for(int i = l; i < L; ++i)
        dsu.rollback();
    for(int i = R + 1; i <= r; ++i)
        dsu.rollback();

    return ans;
}

int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);

    // Input
    cin >> n >> m >> q;
    a.resize(n);
    x.resize(m);
    y.resize(m);
    l.resize(q);
    r.resize(q);
    ans.resize(q, -1);
    for(int i = 0; i < n; ++i) cin >> a[i];
    for(int i = 0; i < m; ++i) cin >> x[i] >> y[i], --x[i], --y[i];
    for(int i = 0; i < q; ++i) cin >> l[i] >> r[i], --l[i], --r[i];

    // Solve
    DSU dsu(n, a);

    vector<vector<vector<int>>> Query(blockIDof(m-1) + 1, vector<vector<int>>(blockIDof(m-1) + 1, vector<int>(0)));
    for(int i = 0; i < q; ++i) {
        if(blockIDof(l[i]) + 1 <= blockIDof(r[i]) - 1)
            Query[blockIDof(l[i]) + 1][blockIDof(r[i]) - 1].pb(i);
        else ans[i] = solveEdgeCase(dsu, l[i], r[i]);
    }

    for(int i = 0; i < Query.size(); ++i) {
        dsu.reset();
        for(int j = i; j < Query.size(); ++j) {
            for(int k = idsOfBlock(j).fi; k <= idsOfBlock(j).se && k < m; ++k) {
                dsu.join(x[k], y[k]);
            }
            assert(i < Query.size() && j < Query[i].size());
            for(int id : Query[i][j])
                ans[id] = solveCompressedCase(dsu, l[id], r[id]);
        }
    }

    // Output
    for(int i = 0; i < q; ++i) assert(ans[i] != -1);
    for(int i = 0; i < q; ++i) cout << ans[i] << '\n';

    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.