算法随笔——线段树分治

算法随笔——线段树分治

例题

有一个 n 个节点 m 条边的无向图,对于一条边有四个参数 (a,b,l,r) ,表示这条边在 [l,r] 这些时间连接 (a,b) 。

有一个传送技能:如果在某时刻 u 和 v 在一个连通块里,可以从 u 传送到 v 。

小 A 初始在节点 1 ,所以 小 A 想知道他能在 [1,n] 中的哪些时间点能直接从 1 传送到节点 i ,请你帮帮他。

大概是用线段树维护时间上的操作序列。

以时间为轴建一棵线段树,将操作序列放上去,

则每个对于时间区间操作可被分割放到 \(O(\log n)\) 个区间上

于是我们在线段树上dfs

每次遇到一个点便将他身上的操作区间加上

递归结束时便撤销

当 dfs 到叶子节点 \(i\) 时,此时被加入的操作刚好是所有覆盖了时间点 \(i\) 的操作。

于是我们将加入和删除操作变为了加入和撤销,可以用可撤销并查集做到。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define int ll
#define PII pair<int,int>
#define rep(k,a,b) for (int k = a;k <= b;k++)
#define addev(a,b,c) v[a].push_back({b,c});
#define rd read
#define all(a) a.begin(),a.end()
#define mem(a,b) memset(a,b,sizeof a);
#define pb push_back
#define vct vector
#define rev(T) reverse(T.begin(),T.end())

int read()
{
	int f=1,k=0;char c = getchar();
	while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
	return k*f;
}

const int N = 6e5+5;

int n,m;
vector<int> son[N];
vct<array<int,2> > v[N<<2];

struct node
{
	int a,b,l,r;
}e[N];
struct DSU
{
	int f[N],tag[N],siz[N];
	stack<PII> pool;
	DSU() 
	{
		rep(i,1,N-2) f[i] = i,tag[i] = 0,siz[i] = 1;
	} 
	
	int find(int x)
	{
	//	cout << x << ' ' << f[x] << endl;
		if (f[x] != x) return find(f[x]);
		return f[x];
	}
	bool merge(int x,int y)
	{
		int fx = find(x),fy = find(y);
		if (fx == fy) return 0;
		if (siz[fx] > siz[fy]) swap(fx,fy);
		pool.push({fx,fy});
		f[fx] = fy;
		siz[fy] += siz[fx];
		tag[fx] -= tag[fy];		 
		return 1;
	}
	void cancel()
	{
		auto [x,y] = pool.top();pool.pop();
		siz[y] -= siz[x];
		f[x] = x;
		tag[x] += tag[y]; 
	}
	
	
}dsu;



void adde(int k,int l,int r,int x,int y,int a,int b)
{
	if(x > r || y < l) return;
	if (x <= l && r <= y)
	{
		v[k].pb({a,b});
		return;
	}
	int mid = l + r>>1;
	adde(k<<1,l,mid,x,y,a,b);adde(k<<1|1,mid+1,r,x,y,a,b);
}

void solve(int k,int l,int r)
{
	int cnt = 0;
	for (auto [a,b] : v[k])
	{
	//	cout << "ab:" << a << ' ' << b << endl;
		cnt += dsu.merge(a,b);
	}
//	cout << "fuck" << endl;
	
	if (l == r)
	{
//		cout << "cur:"<<l << endl;
//		for (int i = 1;i <= n;i++) cout << dsu.f[i] << ' ';cout<<endl;
		int f1 = dsu.find(1);
	//	cout << l << ' ' << f1 << endl;
		dsu.tag[f1] += l;
		rep(i,1,cnt) dsu.cancel();
	//	cout << "666" << endl;
		return;
	}
	int mid= l+ r >> 1;
	solve(k<<1,l,mid);solve(k<<1|1,mid+1,r);
	rep(i,1,cnt) dsu.cancel();
}




void dfs(int x,int fa)
{
	if (fa!= x) dsu.tag[x] += dsu.tag[fa];
	for (auto y : son[x])
	{
		if (y == fa) continue;
		dfs(y,x);
	}
}

void solvemain()
{
	cin >> n >> m;
	
	rep(i,1,m) cin >> e[i].a >> e[i].b >> e[i].l >> e[i].r;
	rep(i,1,m) adde(1,1,n,e[i].l,e[i].r,e[i].a,e[i].b);
// 	
	solve(1,1,n);
//	cout << 666 << endl;
// 	
// 	
	 for (int i = 1;i <= n;i++) son[dsu.f[i]].pb(i);
	
	 for (int i = 1;i <= n;i++) if (dsu.f[i] == i) dfs(i,i);
// 	
// 	
	 int ans = 0;
	 for (int i= 1;i <= n;i++) 
	 {
//	 cout << dsu.tag[i] << '\n';
	 ans ^= dsu.tag[i];
	 }
	 cout << ans << '\n';
	
}

signed main()
{
//	cout << "fuck"<<endl;
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;t = 1;
	while(t--)
	{
		solvemain();
	}
	return 0;
}
posted @ 2025-08-02 21:26  codwarm  阅读(7)  评论(0)    收藏  举报