Hướng dẫn giải của duong3982oj Contest 03 - Cặp số chia hế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.

Tác giả: duong3982, huyjavalt

Subtask 1

Với cây là đường thẳng, chúng ta có thể duyệt mọi cặp ~i~, ~j~ và tính toán.

Độ phức tạp: ~O~ (~n^2 \times q~).

Subtask 2

Cải tiến từ subtask 1, chúng ta có thể ~DFS~ hoặc ~BFS~ để tìm ra đường đi, sau đó vẫn duyệt ~i~ và ~j~ như subtask 1.

Độ phức tạp: ~O~ (~n^2 \times q~).

Subtask 3

Với ~a_i = a_j~, dễ thấy đáp án là ~x^2~ với ~x~ là số đỉnh trên đường đi đơn từ ~i~ đến ~j~.

Để tìm được ~x~, ta sẽ coi ~1~ là gốc của cây, và ~d_i~ là khoảng cách từ ~1~ đến đỉnh ~i~.

Ta sẽ dễ dàng tìm được ~x~ bằng cách tìm tổ tiên chung gần nhất.

Độ phức tạp: ~O~ (~n \times log (n)~).

Subtask 5

Ý tưởng đơn giản từ thuật toán MO. Để giải quyết các truy vấn ta có mảng ~choose_i~ là số con trong đoạn hiện tại chia hết cho i và ~cnt_i~ là số con có giá trị ~i~ trong đoạn hiện tại.

Khi ta thêm 1 giá trị ~x~ vào thì đáp án sẽ tăng lên bằng ~choose_x~ + ~\sum cnt_y~ (với y là ước của x khác x) + ~cnt_x * 2 - 1~ và tương tự với việc bỏ giá trị.

Subtask 6

Thay vì sử dụng Mo Algorithm trên đoạn thẳng, ta sẽ sử dụng Mo Algorithm trên cây.

Template MO trên cây được lấy từ blog https://codeforces.com/blog/entry/43230

Code mẫu:

#include <bits/stdc++.h>

using namespace std;

#define int long long

int a[40005];
vector<int> adj[40005];
pair<int,int> qr[40005];
vector<int> divi[40005];

const int l = 16;

int timer = 1;
int in[40005], out[40005], id[80005];
int up[40005][20];

void dfs(int v, int p){
    id[timer] = v;
    in[v] = timer++;
    up[v][0] = p;
    for(int i = 1; i <= l; i++) up[v][i] = up[up[v][i-1]][i-1];
    for(auto u : adj[v]) if(u != p) dfs(u,v);
    id[timer] = v;
    out[v] = timer++;
}

bool is_anc(int u, int v){
    return in[u] <= in[v] && out[v] <= out[u];
}

int lca(int u, int v){
    if(is_anc(u,v)) return u;
    if(is_anc(v,u)) return v;
    for(int i = l; i >= 0; i--){
        if(!is_anc(up[u][i],v)){
            u = up[u][i];
        }
    }
    return up[u][0];
}

int choose[40005];
int ans[40005];
int cnt[40005];

int n,q;

int block;

struct Qury{
    int l,r,id,lc;
    bool operator<(const Qury& other) const{
        if(l/block != other.l/block) return l/block < other.l/block;
        return r < other.r;
    }
};

Qury query[40005];
int vis[40005];
int check[40005];

void add(int i, int& res){
    res += choose[a[i]];
    for(auto j : divi[a[i]]){
        //cout << j << ' ' << a[i] << '\n';
        if(j != a[i]){
            choose[j]++;
            res += cnt[j];
        }
    }
    cnt[a[i]]++;
    res += cnt[a[i]]*2-1;
    //cout << "ADD" << ' ' << i << ' ' << res << endl;
}

void del(int i, int& res){
    res -= choose[a[i]];
    for(auto j : divi[a[i]]){
        if(cnt[j] && j != a[i]) res -= cnt[j];
        if(j != a[i]) choose[j]--;
    }
    res -= cnt[a[i]]*2-1;
    cnt[a[i]]--;
    //cout << "REV" << ' ' << i << ' ' << res << endl;
}

void cal(int i, int& res){
    //cout << i << endl;
    if(vis[i]) del(i,res);
    else add(i,res);
    vis[i] ^= 1;
}

signed main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        check[a[i]] = 1;
    }

    for(int i = 1; i <= 4e4; i++) if(check[i]){
        for(int j = i; j <= 4e4; j += i) if(check[j]){
            divi[j].push_back(i);
        }
    }

    for(int i = 2; i <= n; i++){
        int u,v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    dfs(1,1);

    block = sqrtl(n);
    for(int i = 1; i <= q; i++){
        int u,v;
        cin >> u >> v;
        if(in[u] > in[v]) swap(u,v);
        int r = lca(u,v);
        if(r == u) query[i] = {in[u],in[v],i,r};
        else query[i] = {out[u],in[v],i,r};
    }
    sort(query+1, query+q+1);

    int curl = query[1].l, curr = query[1].l-1;
    int res = 0;
    for(int k = 1; k <= q; k++){
        int l = query[k].l, r = query[k].r;
        //cout << l << ' ' << r << ' ' << id[l] << ' ' << id[r] << ' ' << query[k].lc << endl;
        while(curl < l){
            cal(id[curl],res);
            curl++;
        }
        while(curl > l){
            curl--;
            cal(id[curl],res);
        }
        while(curr < r){
            curr++;
            cal(id[curr],res);
        }
        while(curr > r){
            cal(id[curr],res);
            curr--;
        }

        if(query[k].lc != id[l] && query[k].lc != id[r]) cal(query[k].lc,res);
        ans[query[k].id] = res;
        if(query[k].lc != id[l] && query[k].lc != id[r]) cal(query[k].lc,res);
    }

    for(int k = 1; k <= q; k++) cout << ans[k] << '\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.