题目链接
题目大意
解题思路:
我一开始想到可以用可撤销并查集去维护这种删边加边的操作,但是有个缺点是每次撤销都有把后面的边全部撤销复度是O(n2)O(n^2)O(n2)
- 首先我们考虑这种动态加边删边的问题,如果是离线的话,那就是线段树分治+可撤销的并查集的模板题
- 但是这是强制在线,但是这是虚伪的强制在线就是,每次个操作最多有两种的结果。
lst={0,1}lst=\{0,1\}lst={0,1}, 最多也就2×m2\times m2×m个操作 - 就是我们有条有若干段存在的时间,我们插进线段树里面,用线段树去维护撤销顺序
- 首先我们先把两种结果都插进线段树里面,注意线段树分治本质是一个半在线算法
- 严格来说我们一开始只是把操作给划分成logloglog块,但是没有调用并查集
- 最后一次对整个线段树来个整题的遍历,实际上线段树上面的遍历其实也和你在线处理没什么去区别,你用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) { // 插销dsuauto 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;
}