Submission #1306888

#TimeUsernameProblemLanguageResultExecution timeMemory
1306888fafnirWerewolf (IOI18_werewolf)C++20
49 / 100
4094 ms30200 KiB
#include <bits/stdc++.h>
#include "werewolf.h"
using namespace std;

#define REP(i, n) for (int i = 0; i < (n); i++)

template<typename T>
struct RMin {
    T operator()(const T& a, const T& b) {
        if (a < b) return a;
        return b;
    }
};

template<typename T>
struct RMax {
    T operator()(const T& a, const T& b) {
        if (a > b) return a;
        return b;
    }
};


// Op: Aggregation (min/max/sum/gcd)
// T:  Underlying type
// For non idempotent aggregation (e.g. sum), need query_log,
// else query_const
template<typename T, typename Op>
class SparseTable {
private:
    Op m_op;
    vector<vector<T>> m_table;
    size_t m_n;
    size_t m_log;
    void build(const vector<T>& data) {
        copy(data.begin(), data.end(), m_table[0].begin());
        for (int i = 1; i <= m_log; ++i) {
            for (int j = 0; j + (1 << i) <= m_n; ++j) {
                m_table[i][j] = m_op(m_table[i-1][j], m_table[i-1][j + (1 << (i-1))]);
            }
        }
    }
    int log2_floor(unsigned long i) {
        return i ? 63 - __builtin_clzll(i) : -1;
    }
public:
    SparseTable(vector<T>& data, Op op) : m_n{data.size()}, m_log{0}, m_op{op} {
        while ((1 << (m_log+1)) <= m_n) {
            ++m_log;
        }
        m_table.resize(m_log + 1, vector<T>(m_n));
        build(data);
    }
    T query_const(int l, int r) {
        int i = log2_floor(r - l + 1);
        return m_op(m_table[i][l],
                m_table[i][r - (1 << i) + 1]);
    }

    T query_log(int l, int r) {
        T res{};
        for (int i = m_log; i >= 0; --i) {
            if ((1 << i) <= r - l + 1) {
                res = m_op(res, m_table[i][l]);
                l += 1 << i;
            }
        }
        return res;
    }
};


// Two ranges [l0, r0]
//            [l1, r1]
// Intersect if there is x s.t. l0 <= x <= r0 and l1 <= x <= r1
// Assuming l1 <= r1 and l0 <= r0
// l0 <= r1 && l1 <= r0
bool range_intersect(const pair<int,int>& a, const pair<int,int>& b) {
    return a.first <= b.second && b.first <= a.second;
}



vector<bool> reachable(vector<vector<int>>& adjList, int start, int lo, int hi) {
    const int N = adjList.size();
    vector<bool> vis(N, false);

    queue<int> q;
    if (start >= lo && start <= hi) {
        q.push(start);
        vis[start] = true;
    }
    while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (auto& v : adjList[u]) {
            if (lo <= v && v <= hi && !vis[v]) {
                vis[v] = true;
                q.push(v);
            }
        }
    }

    return vis;
}

// Invariant: Interval [up,x] valid
int find_left(SparseTable<int, RMin<int>>& st_min,
    SparseTable<int, RMax<int>>& st_max, int x, int l, int r) {
    int lb = 0;
    int up = x;
    while (lb != up) {
        int s = (lb + up) / 2;

        int mn = st_min.query_const(s, x);
        int mx = st_max.query_const(s, x);

        if (mn >= l && mx <= r) {
            up = s;
        } else {
            lb = s+1;
        }
    }
    return up;
}

// Invariant: Interval [x,lb] valid
int find_right(SparseTable<int, RMin<int>>& st_min,
    SparseTable<int, RMax<int>>& st_max, int x, int l, int r, int N) {
    int lb = x;
    int up = N-1;
    while (lb != up) {
        int s = (lb + up + 1) / 2;

        int mn = st_min.query_const(x, s);
        int mx = st_max.query_const(x, s);

        if (mn >= l && mx <= r) {
            lb = s;
        } else {
            up = s-1;
        }
    }
    return lb;
}


int fufind(vector<int>& fu, int x) {
    if (fu[x] < 0) {return x;}      // x is root
    fu[x] = fufind(fu, fu[x]);
    return fu[x];
}

void fujoin(vector<int>& fu, vector<int>& pos_sml, vector<int>& pos_big, int x, int y) {
    x = fufind(fu, x);
    y = fufind(fu, y);

    if (x != y) {
        fu[y] = x;
        pos_sml[x] = min(pos_sml[x], pos_sml[y]);
        pos_big[x] = max(pos_big[x], pos_big[y]);
    }
}


vector<int> check_validity(int N, vector<int> X, vector<int> Y,
                            vector<int> S, vector<int> E,
                            vector<int> L, vector<int> R) {
    int Q = S.size();
    int M = X.size();
    
    vector<int> A(Q);
    vector<vector<int> > adj(N);
    REP(i, M) {
        adj[X[i]].push_back(Y[i]);
        adj[Y[i]].push_back(X[i]);
    }

    // Check for line graph
    bool isLine = true;

    REP(i, N) {
        isLine = isLine && (adj[i].size() <= 2);
    }

    // Subtask 3: Line graph
    // DSU solution
    if (isLine) {

        // Linearize graph
        int u = -1;
        int parent = -1;
        REP(i, N) {
            if (adj[i].size() == 1) {u = i; break;}
        }

        vector<int> order(N);
        vector<int> pos(N);
        REP(i, N) {
            order[i] = u;
            pos[u] = i;
            int next = (adj[u][0] == parent);
            parent = u;
            u = adj[u][next];
        }


        // Idea: Define V_L as set of vertices reachable from S
        //       that have values in range [L,N-1]
        //       They form an interval surrounding S
        
        vector<int> fu(N);
        vector<int> pos_sml(N);
        vector<int> pos_big(N);
        vector<pair<int,int>> Lrange(Q);
        vector<pair<int,int>> Rrange(Q);

        // Sort queries by L[i] (non-increasing)
        vector<int> idx(Q);
        REP(i, Q) {idx[i] = i;}
        sort(idx.begin(), idx.end(), [&](int i0, int i1) {
            return L[i0] > L[i1];
        });

        // Initialize DSU structure
        REP(i, N) {
            fu[i] = -1;
            pos_sml[i] = pos_big[i] = pos[i];
        }


        // Calculate V_L 
        int next = N-1;
        REP(i, Q) {
            int limit = L[idx[i]];
            for (; next >= limit; --next) {
                for (int u : adj[next]) {
                    if (u >= limit) {
                        fujoin(fu, pos_sml, pos_big, next, u);
                    }
                }
            }

            int lead = fufind(fu, S[idx[i]]);
            Lrange[idx[i]] = make_pair(pos_sml[lead], pos_big[lead]);
        }


        // Calculate V_R
        REP(i, Q) {idx[i] = i;}
        vector<int> idx1(Q);
        REP(i, Q) {idx1[i] = i;}
        sort(idx1.begin(), idx1.end(), [&](int i0, int i1) {
            return R[i0] < R[i1];
        });
        REP(i, N) {
            fu[i] = -1;
            pos_sml[i] = pos_big[i] = pos[i];
        }
        next = 0;
        REP(i, Q) {
            int limit = R[idx1[i]];
            for(; next <= limit; ++next) {
                for (int u : adj[next]) {
                    if (u <= limit) {
                        fujoin(fu, pos_sml, pos_big, next, u);
                    }
                }
            } 

            int lead = fufind(fu, E[idx1[i]]);
            Rrange[idx1[i]] = make_pair(pos_sml[lead], pos_big[lead]);
        }


        REP(i,Q) {
            A[i] = range_intersect(Lrange[i], Rrange[i]);
        }


    } else {

        REP(i, Q) {
            vector<bool> reach_S = reachable(adj, S[i], L[i], N-1);
            vector<bool> reach_E = reachable(adj, E[i], 0, R[i]);
                
            REP(v, N) {
                A[i] |= reach_S[v] && reach_E[v];
                if (A[i]) {break;}
            }
        }

    }

    return A;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...