Submission #1271784

#TimeUsernameProblemLanguageResultExecution timeMemory
1271784thuhienneBridges (APIO19_bridges)C++17
0 / 100
3095 ms6032 KiB
#include <bits/stdc++.h>
using namespace std;

/*
   DSU rollback + block decomposition
   Cải tiến so với code gốc:
   - dùng mảng tĩnh cho record stack (không dùng vector)
   - tag/version để mark changed edges trong block (reset O(1))
   - một vài micro-optimizations
*/

#define SIZE __size__
#define UNION __union__
#define DATA __data__

const int MAXM = 100009;
const int MAXQ = 100009;
const int MAXN = 100009;
const int CAN = 350; // kích thước block, có thể tune

int n, m, q;

struct Edge {
    int u, v, w, index;
} edge[MAXM];

bool dummy_changed[MAXM]; // không dùng trực tiếp, dùng tag below

// tag/version để biết edge bị thay trong block
int tag_changed[MAXM];
int cur_tag = 1;

// để lưu lần đầu xử lý trong block (firsttime logic)
int firsttime[MAXM];
int uniq = 1;

int root_arr[MAXN], sz_arr[MAXN];

int getroot_simple(int x) {
    return (root_arr[x] == x ? x : root_arr[x] = getroot_simple(root_arr[x]));
}
void union_simple(int a, int b) {
    a = getroot_simple(a);
    b = getroot_simple(b);
    if (a == b) return;
    root_arr[a] = b;
    sz_arr[b] += sz_arr[a];
}

// DSU rollback record entry (mảng tĩnh)
struct REC {
    int node;         // node index whose parent/size changed
    int root_before;  // previous parent
    int size_before;  // previous size[node]
};
const int MAX_RECORDS = 2000000; // tune nếu cần
REC record_stack[MAX_RECORDS];
int rec_top = 0;

// rollback-aware getroot (path comp without destroying info)
int getroot_record(int x) {
    while (root_arr[x] != x) x = root_arr[x];
    return x;
}

// version of getroot that records changes for rollback
int getroot_and_record(int x) {
    if (root_arr[x] == x) return x;
    int p = getroot_and_record(root_arr[x]);
    // record current node's parent and size before changing
    record_stack[rec_top++] = { x, root_arr[x], sz_arr[x] };
    root_arr[x] = p;
    return p;
}

void union_record(int a, int b) {
    a = getroot_and_record(a);
    b = getroot_and_record(b);
    if (a == b) return;
    // record a before linking
    record_stack[rec_top++] = { a, root_arr[a], sz_arr[a] };
    root_arr[a] = b;
    // record b before size update
    record_stack[rec_top++] = { b, root_arr[b], sz_arr[b] };
    sz_arr[b] += sz_arr[a];
}

void rollback_to(int old_top) {
    while (rec_top > old_top) {
        --rec_top;
        REC r = record_stack[rec_top];
        root_arr[r.node] = r.root_before;
        sz_arr[r.node] = r.size_before;
    }
}

struct Query {
    int op, x, y, index;
} ask[MAXQ];

Edge bridges[MAXQ]; // store edges for updates inside block
int answer[MAXQ];

// helper array for sorting edges by weight (indices)
int order_idx[MAXM];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    if (!(cin >> n >> m)) return 0;
    for (int i = 1; i <= m; ++i) {
        cin >> edge[i].u >> edge[i].v >> edge[i].w;
        edge[i].index = i;
    }
    cin >> q;
    
    // init tags/firsttime
    for (int i = 1; i <= m; ++i) {
        tag_changed[i] = 0;
        firsttime[i] = 0;
    }
    
    // process queries block by block
    int block_id = 0;
    for (int start = 1; start <= q; start += CAN) {
        ++block_id;
        int l = start;
        int r = min(q, start + CAN - 1);
        
        // read block queries
        for (int i = l; i <= r; ++i) {
            cin >> ask[i].op >> ask[i].x >> ask[i].y;
            ask[i].index = i;
            if (ask[i].op == 1) {
                // mark changed using tag
                tag_changed[ask[i].x] = block_id;
                bridges[i] = edge[ask[i].x]; // store prior edge state
            }
        }
        
        // Build an index array of edges sorted by current weight
        // (We will skip edges that are changed in this block via tag_changed)
        for (int i = 1; i <= m; ++i) order_idx[i-1] = i;
        sort(order_idx, order_idx + m, [&](int a, int b){
            if (edge[a].w != edge[b].w) return edge[a].w < edge[b].w;
            return a < b;
        });
        
        // init DSU
        for (int i = 1; i <= n; ++i) {
            root_arr[i] = i; sz_arr[i] = 1;
        }
        
        // collect queries of type 2 (contain) with their index and sort descending by y
        static Query contain[CAN + 5];
        int contain_cnt = 0;
        for (int i = l; i <= r; ++i) {
            if (ask[i].op == 2) contain[contain_cnt++] = ask[i];
        }
        sort(contain, contain + contain_cnt, [](const Query &a, const Query &b){
            return a.y > b.y; // descending by y
        });
        
        // process contain queries using pointer on sorted edges (descending)
        int ptr = m - 1; // order_idx[ptr] is current largest weight candidate
        for (int ci = 0; ci < contain_cnt; ++ci) {
            Query d = contain[ci];
            int minimum = d.y;
            int island = d.x;
            
            // add all global edges (not changed in this block) with w >= minimum
            while (ptr >= 0 && edge[order_idx[ptr]].w >= minimum) {
                int ei = order_idx[ptr];
                if (tag_changed[ei] != block_id) {
                    union_simple(edge[ei].u, edge[ei].v);
                }
                --ptr;
            }
            
            // now we need to incorporate changed edges from this block
            int old_top = rec_top;
            ++uniq;
            // scan left of query in block
            for (int i = d.index - 1; i >= l; --i) {
                if (ask[i].op != 1) continue;
                Edge br = bridges[i];
                int w_for_this = ask[i].y;
                int idx = br.index;
                if (firsttime[idx] == uniq) continue;
                firsttime[idx] = uniq;
                if (w_for_this >= minimum) union_record(br.u, br.v);
            }
            // scan right of query in block
            for (int i = d.index + 1; i <= r; ++i) {
                if (ask[i].op != 1) continue;
                Edge br = bridges[i];
                int w_for_this = ask[i].y;
                int idx = br.index;
                if (firsttime[idx] == uniq) continue;
                firsttime[idx] = uniq;
                if (br.w >= minimum) union_record(br.u, br.v);
            }
            // but note: for updates that appear in block, there are two relevant weights:
            // - for updates before d.index, the effective weight is ask[i].y (the new weight)
            // - for updates after d.index, the effective weight is br.w (previous weight)
            // The code above handles that: left uses ask[i].y, right uses br.w.
            
            // answer: size of component containing island
            int comp_root = getroot_simple(island);
            // However, some nodes may have been path-compressed in union_record; to get correct size,
            // we should get root via getroot_and_record or getroot_simple (we maintained sz only for roots)
            answer[d.index] = sz_arr[getroot_simple(island)];
            
            // rollback to old_top
            rollback_to(old_top);
        }
        
        // after processing block, apply updates (op==1) permanently to edges
        for (int i = l; i <= r; ++i) {
            if (ask[i].op == 1) {
                int ei = ask[i].x;
                edge[ei].w = ask[i].y;
                // clear tag by just leaving it; next block uses different block_id
            }
        }
    }
    
    // print answers for op==2
    for (int i = 1; i <= q; ++i) {
        if (ask[i].op == 2) {
            cout << answer[i] << '\n';
        }
    }
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...