cppAcademy
FROM LEARNING TO COMPETING

Expert.
Put your skills to the test.

Bulgarian Informatics Group A · 2009–2026

217 independently explained problems: 54 Municipal, 54 Regional and 109 National. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group A also includes challenging national-level problems. The 109 National-round tasks shared by Groups A and B retain their existing permanent URLs and explanations. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP A2013 · Very hardDynamic connectivity

Emergency Number 112

National Olympiad — Regional round (2013 season)

Submission name: no122A.cpp

Problem summary

Maintain an undirected road network. Calls ask whether two towns are connected (type 1), open a road (type 2), or close a road (type 3). Output the answers to all connectivity queries as one bit string.

Source clarification. Arena slug is no122A, but the PDF calls the program no112A and the Bulgarian title is ТЕЛЕФОН 112. Roads are interpreted as undirected, with open/closed status per unordered pair.

Input

N M, M initial roads, Q, then Q triples type x y.

Output

One bit per type-1 call: 1 if connected, otherwise 0.

Constraints

N ≤ 1000; M ≤ 2N; Q ≤ 100000. The statement describes about 30–35% openings and about one tenth as many closures as openings.

Worked example

Official statement sample.

Input

9 8
1 2
3 4
5 6
7 8
9 5
7 2
8 2
6 9
8
1 1 8
1 6 2
2 7 1
1 4 7
2 2 3
1 4 7
3 3 4
1 4 7

Output

10010

Solution analysis

  1. Process the calls offline. For each unordered road pair record each maximal interval of query indices during which it is open. Initially open roads begin at time zero; an opening begins an interval, and a closure ends it immediately before that call.
  2. Put each active interval into a segment tree over time. Traverse the tree with a disjoint-set union structure that supports rollback. At a node unite its roads, answer queries at leaves, and undo the node’s merges before returning to its parent. Union by size gives logarithmic find time without path compression.
  3. In the sample towns 1 and 8 are initially connected, while 6 and 2 are not. Opening road 2–3 later connects 4 to 7, and closing 3–4 disconnects 4 again, producing 10010.

Why it works

The segment-tree cover places a road on exactly one node of every root-to-leaf path whose time lies in its active interval, and on no other such path. Thus at a leaf the DSU represents exactly the currently open graph. DSU connectivity is exact, and rollback restores the state required by each sibling subtree.

Complexity: O((M+Q) log Q log N) time and O((M+Q) log Q+N) space.

Watch out. Path compression must not be used with this simple rollback log. Normalize road endpoints. Duplicate open/close notifications are treated idempotently.

Commented C++17 solution

An independently written complete program with standard headers.

#include<iostream>
#include<vector>
#include<map>
#include<utility>
#include<numeric>
#include<algorithm>
using namespace std;
using Edge=pair<int,int>;
struct DSU{
    vector<int>parent,size;
    vector<Edge>history;
    explicit DSU(int n):parent(n+1),size(n+1,1){
        iota(parent.begin(),parent.end(),0);
    }
    int find(int x){
        while(parent[x]!=x){
            x=parent[x];
        }
        return x;
    }
    void unite(int a,int b){
        a=find(a);
        b=find(b);
        if(a==b){
            return;
        }
        if(size[a]<size[b]){
            swap(a,b);
        }
        history.push_back({a,b});
        parent[b]=a;
        size[a]+=size[b];
    }
    void rollback(int checkpoint){
        while(static_cast<int>(history.size())>checkpoint){
            auto[a,b]=history.back();
            history.pop_back();
            size[a]-=size[b];
            parent[b]=b;
        }
    }
};
struct Call{
    int type,x,y;
};
vector<vector<Edge>>tree;
vector<Call>calls;
void insert(int node,int left,int right,int from,int to,Edge e){
    if(from>=right||to<=left){
        return;
    }
    if(from<=left&&right<=to){
        tree[node].push_back(e);
        return;
    }
    int mid=(left+right)/2;
    insert(node*2,left,mid,from,to,e);
    insert(node*2+1,mid,right,from,to,e);
}
void solve(int node,int left,int right,DSU&dsu){
    // Roll back all merges made in this subtree before visiting its sibling.
    int checkpoint=dsu.history.size();
    for(auto[a,b]:tree[node]){
        dsu.unite(a,b);
    }
    if(right-left==1){
        const Call&c=calls[left];
        if(c.type==1){
            cout<<(dsu.find(c.x)==dsu.find(c.y)?'1':'0');
        }
    }
    else{
        int mid=(left+right)/2;
        solve(node*2,left,mid,dsu);
        solve(node*2+1,mid,right,dsu);
    }
    dsu.rollback(checkpoint);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,m;
    cin>>n>>m;
    map<Edge,int>opened;
    while(m--){
        int x,y;
        cin>>x>>y;
        opened[minmax(x,y)]=0;
    }
    int q;
    cin>>q;
    calls.resize(q);
    tree.resize(4*max(1,q));
    for(int i=0;i<q;++i){
        Call&c=calls[i];
        cin>>c.type>>c.x>>c.y;
        Edge e=minmax(c.x,c.y);
        if(c.type==2&&!opened.count(e)){
            opened[e]=i;
        }
        if(c.type==3&&opened.count(e)){
            insert(1,0,q,opened[e],i,e);
            opened.erase(e);
        }
    }
    if(q){
        for(const auto&entry:opened){
            insert(1,0,q,entry.second,q,entry.first);
        }
        DSU dsu(n);
        solve(1,0,q,dsu);
    }
    cout<<'\n';
}

Source: Arena Olimpiici ↗