洛谷5787 (线段树分治)
例题:有一个 $n$个节点的图,在 $k$时间内有 $m$条边会出现后消失,要求出每一时间段内这个图是否是二分图。
输入格式:第一行三个整数 $n, m, k$,接下来 m行,每行四个整数 $x, y, l, r$,表示有一条连接 $x, y$的边在 $l$时刻出现 $r$时刻消失。
输出格式: $k$行,第 $i$行一个字符串 Yes
或 No
,表示在第 i时间段内这个图是否是二分图
分析:在时间轴上建一棵线段树,这样对于每个操作,相当于在线段树上进行区间操作。对于每条边,将它按照线段树区间操作的方式划分成 O(logk)段,用 vector
挂在线段树的节点上。遍历时,从根节点出发,每到一个节点,将挂在该节点上的所有边合并,然后递归处理左儿子和右儿子。如果发现有某条边合并会出现奇环,那么当前线段树节点所对应的时间区间都不会形成二分图。当到达叶子节点时,如果合并了所有挂在当前节点上的边,依旧满足二分图的性质,那么可以直接输出 Yes
。回溯时,由于并查集不支持删边,我们可以使用可撤销并查集,即用一个栈记录下所有对并查集的操作。由于可撤销,因此不能路径压缩,为保证复杂度,必须按秩合并。
#include<cstdio> #include<vector> #include<stack> #define fi first #define se second #define mid ((l + r) >> 1) using namespace std; const int N = 1e5 + 5; const int M = 2e5 + 5; typedef pair<int, int> pii; int n, m, k; int x[M], y[M], l[M], r[M], fa[N << 1], dep[N]; vector<int> vt[N << 4]; stack<pii> sta; int find(int u) { return u == fa[u] ? u : find(fa[u]); } inline void swap(int &x, int &y) { x ^= y ^= x ^= y; } void ins(int u, int l, int r, int cl, int cr, int id){ if(cl <= l && cr >= r){ vt[u].push_back(id); return ; } if(cl <= mid) ins(u << 1, l, mid, cl, cr, id); if(cr > mid) ins(u << 1 | 1, mid + 1, r, cl, cr, id); } void merge(int u, int v){ if(dep[u] < dep[v]) swap(u, v); sta.push(make_pair(v, dep[u] == dep[v])); fa[v] = u, dep[u] += (dep[u] == dep[v]); } void solve(int u, int l, int r){ bool vis = 1; int siz = sta.size(); for(auto &v : vt[u]){ int k = find(x[v]), kk = find(y[v]); if(k == kk){ vis = 0; for(int i = l; i <= r; ++i) puts("No"); break; } merge(find(x[v]), find(y[v] + N)), merge(find(x[v] + N), find(y[v])); } if(vis){ if(l == r) puts("Yes"); else{ solve(u << 1, l, mid); solve(u << 1 | 1, mid + 1, r); } } while(sta.size() > siz){ pii tt = sta.top(); sta.pop(); dep[fa[tt.fi]] -= tt.second; fa[tt.fi] = tt.fi; } } int main(){ scanf("%d%d%d", &n, &m, &k); for(int i = 1; i <= m; ++i) scanf("%d%d%d%d", &x[i], &y[i], &l[i], &r[i]); for(int i = 1; i <= n; ++i) fa[i] = i, fa[i + N] = i + N; for(int i = 1; i <= m; ++i) if(l[i] != r[i]) ins(1, 1, k, l[i] + 1, r[i], i); solve(1, 1, k); return 0; }
你只有十分努力,才能看上去毫不费力。