[CCO 2024] Telephone Plans 题解

[CCO 2024] Telephone Plans 题解


知识点

启发式合并,启发式分裂,BFS,LCT。

分析

首先有“所有边连接起来会得到森林”和“所有边只会出现一段连续的时间”两个重要条件。

然后对于连边和删边就有如下算法:

  • 连边:启发式合并,将较小的连通块接到较大的上面。
  • 删边:启发式分裂,用 BFS 找出大小较小的那边,注意 BFS 时记录的节点是原图的边,否则会导致复杂度错误。

两者的均摊复杂度皆为 \(O(n\log_2{n})\)

最后考虑查询。

我们可以考虑记一个目前总共合并过的节点对数 \(sum\),每次连边时,将两个连通块 \(u,v\) 的大小相乘并加入 \(sum\),即 \(sum\gets sum +siz_u\times siz_v\)

除此之外再记一个 \(d_{i}\) 表示第 \(i\) 次操作使节点对数减少的数量,那么在删边时,对于分裂出来的两个连通块 \(u,v\),将他们相乘并记下即可 \(d_i\gets siz_u\times siz_v\)。如果操作不是删边,则 \(d_i\gets 0\)

那么第 \(q\) 次查询 \(t\) 的答案就是 \(sum-\sum_{i=1}^{q-t}d_i\),对 \(d\) 做前缀和即可解决。

代码

已略去快读快写。

bool vis[N];
int ALWAYS_ONLINE,n,Q,top;
int idx[N],siz[N],st[N];
ll ans(0),sum(0);
ll del[Qr];
gp_hash_table<int,null_type> g[N];

void dfs(int u,int fa,int new_idx) {
	idx[u]=new_idx;
	for(auto &v:g[u])if(v!=fa)dfs(v,u,new_idx);
}

signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(ALWAYS_ONLINE,n,Q);
	FOR(i,1,n)idx[i]=i,siz[i]=1;
	FOR(q,1,Q) {
		int op;
		ll x,y,t;
		I(op),op<3?I(x,y):I(t),del[q]=del[q-1];
		if(ALWAYS_ONLINE)op<3?x^=ans,y^=ans:t^=ans;
		if(op==1) {
			int u(idx[x]),v(idx[y]);
			if(siz[u]>siz[v])swap(u,v),swap(x,y);
			st[++top]=u,sum+=(ll)siz[u]*siz[v],siz[v]+=siz[u],dfs(x,0,v),g[x].insert(y),g[y].insert(x);
		} else if(op==2) {
			int u(idx[x]),v(idx[y]);
			g[x].erase(y),g[y].erase(x);
			stack<int> sx,sy;
			queue< pair<int,Hit> > qx,qy;
			vis[x]=true,sx.push(x);
			if(!g[x].empty())qx.push({x,g[x].begin()});
			vis[y]=true,sy.push(y);
			if(!g[y].empty())qy.push({y,g[y].begin()});
			while(!qx.empty()&&!qy.empty()) {
				auto Update=[&](stack<int> &s,queue< pair<int,Hit> > &q) {
					int u(q.front().first),v(0);
					Hit it(q.front().second);
					q.pop(),v=*it;
					if(!vis[v]) {
						vis[v]=true,s.push(v);
						if(!g[v].empty())q.push({v,g[v].begin()});
					}
					if((++it)!=g[u].end())q.push({u,it});
				};
				Update(sx,qx),Update(sy,qy);
			}
			auto Split=[&](stack<int> &sx,stack<int> &sy) {
				siz[u=st[top--]]=sx.size(),siz[v]-=siz[u],del[q]+=(ll)siz[u]*siz[v];
				while(!sx.empty())idx[sx.top()]=u,vis[sx.top()]=false,sx.pop();
				while(!sy.empty())vis[sy.top()]=false,sy.pop();
			};
			qx.empty()?Split(sx,sy):Split(sy,sx);

		} else O(ans=sum-del[q-t],'\n');
	}
	return 0;
}

posted @ 2025-08-20 19:49  Add_Catalyst  阅读(7)  评论(0)    收藏  举报