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