线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解

题目链接


题目大意

线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解


解题思路:

我一开始想到可以用可撤销并查集去维护这种删边加边的操作,但是有个缺点是每次撤销都有把后面的边全部撤销复度是 O ( n 2 ) O(n^2) O(n2)

  1. 首先我们考虑这种动态加边删边的问题,如果是离线的话,那就是线段树分治+可撤销的并查集的模板题
  2. 但是这是强制在线,但是这是虚伪的强制在线就是,每次个操作最多有两种的结果。
    l s t = { 0 , 1 } lst=\{0,1\} lst={0,1}, 最多也就 2 × m 2\times m 2×m个操作
  3. 就是我们有条有若干段存在的时间,我们插进线段树里面,用线段树去维护撤销顺序
  4. 首先我们先把两种结果都插进线段树里面,注意线段树分治本质是一个半在线算法
  5. 严格来说我们一开始只是把操作给划分成 l o g log log块,但是没有调用并查集
  6. 最后一次对整个线段树来个整题的遍历,实际上线段树上面的遍历其实也和你在线处理没什么去区别,你用map去维护选择的边,因为你每次都是递归到叶子节点里面那么本质上也就是再在线处理,然后在加边的时候没有被选择过的边就不要加
    写了个假板子调了半天

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
int n, m, k;
struct Edge {
	int op, x, y;
}e[maxn];
unordered_map<int,int> mp[maxn];
struct dsu {
	int fa[maxn], siz[maxn];
	struct inf {int fx, fy;};
	vector<inf> stk;
	void init(int n) {
		stk.clear();
		for(int i = 0; i <= n; ++ i) fa[i] = i, siz[i] = 1;
	}
	int find(int x) {
		return fa[x] == x ? x : find(fa[x]);
	}
	void merge(int x, int y) {
		int fx = find(x);
		int fy = find(y);
		if(fx == fy) return;
		if(siz[fx] > siz[fy]) swap(fx,fy);
		fa[fx] = fy;
		siz[fy] += siz[fx];
		stk.push_back({fx,fy});
	}
}dsu;

int las=0;

struct Segtree {
	vector<PII> q[maxn<<2];
	void update(int rt, int l, int r, int L, int R, PII idx) {
		if(L <= l && R >= r) {
			q[rt].push_back(idx);
			return;
		}

		if(L <= mid) update(Lson,L,R,idx);
		if(R > mid) update(Rson,L,R,idx);
	}

	void dfs(int rt, int l, int r) {
		int lasttop = dsu.stk.size();
		for(auto it : q[rt]) {
		   if(!mp[it.first][it.second]) continue;
           dsu.merge(it.first,it.second);
		}		
		if(l == r) { // 叶子节点跟新答案
			int x = (e[l].x + las - 1) % n + 1;
			int y = (e[l].y + las - 1) % n + 1;
			if(x > y) swap(x,y);
			if(e[l].op == 2) {
				las = (dsu.find(x) == dsu.find(y));
				cout << las;
			} else mp[x][y] ^= 1;
		} else {
			dfs(Lson);
			dfs(Rson);
		}
		while((int)dsu.stk.size() > lasttop) { // 插销dsu
			auto it = dsu.stk.back();
			dsu.stk.pop_back();
			dsu.fa[it.fx] = it.fx;
			dsu.siz[it.fy] -= dsu.siz[it.fx];
		}
	}
} sgt;

int main() {
	read(n,m);
	dsu.init(n);
	for(int i = 1; i <= m; ++ i) {
		int op, x, y;
		read(op, x, y);
		e[i] = {op,x,y};
		if(op == 1) {
			for(int j = 0; j <= 1; ++ j) {
				int l = (x + j - 1) % n + 1;
				int r = (y + j - 1) % n + 1;
				if(l > r) swap(l,r);
				if(mp[l].count(r) && mp[l][r] <= i) sgt.update(1,1,m+1,mp[l][r],i,{l,r});
				mp[l][r] = i + 1;
			}
		} 
	}
	for(int i = 1; i <= n; ++ i) {
		for(auto it : mp[i]) 
		  if(it.second != m+1)
			sgt.update(1,1,m+1,it.second,m+1,{i,it.first});

		mp[i].clear();
	}
	sgt.dfs(1,1,m+1);
	return 0;
}
上一篇:E - Moat(状压&DSU)


下一篇:Note -「Dsu On Tree」学习笔记