题解:P11943 [KTSC 2025] 粒子对撞 / particles
为什么是紫?
对于给定的树,割去所有被关闭的点后,对于一个连通块,我们每次选出两个存在粒子的点,如果这两个点路径上还有存在粒子的点就去递归路径上的点,最后一定可以找到一个合法的可消除对,所以说,一个连通块的贡献就是粒子数除以
怎么修改?考虑这样一个算法,割去一条边时,对于分裂出的两个连通块,随便找一个连通块 dfs,如果 dfs 了超过
-
两侧连通块大小都大于
\sqrt n 且 dfs 了一侧。 -
dfs 了大小更小的那个连通块。
-
两侧连通块大小都小于
\sqrt n 且 dfs 了一侧。
注意到一个点在合并过程中所在集合要么大小小于
代码很好写。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+114;
set<int> E[maxn];
int d[maxn];
int fa[maxn];
int tot;
int V[maxn<<1];
int vis[maxn];//0 1 -1
const int warma = 700;
int now;
void dfs(int u,int lst){
now+=d[u];
if(now>warma) return ;
for(int v:E[u]){
if(v!=lst){
dfs(v,u);
if(now>warma) return ;
}
}
}
void solve(int u,int lst,int root){
fa[u]=root;
V[root]+=(vis[u]==1);
for(int v:E[u])
if(v!=lst) solve(v,u,root);
}
int ans;
void Cut(int u,int v){
E[u].erase(v);
E[v].erase(u);
now=0;
dfs(u,-1);
if(now>warma) swap(u,v);
//solve(u)
ans-=V[fa[u]]/2;
++tot;
solve(u,-1,tot);
V[fa[v]]-=V[fa[u]];
ans+=V[fa[v]]/2+V[fa[u]]/2;
}
void initialize(int n, std::vector<int> A, std::vector<int> B){
for(int i=0;i<n-1;i++){
E[A[i]].insert(B[i]);
E[B[i]].insert(A[i]);
}
for(int i=0;i<n;i++) d[i]=E[i].size();
++tot;
solve(0,-1,tot);
}
int generate(int u, bool result){
if(result==false){
while(E[u].size()>0){
int v=(*E[u].begin());
Cut(u,v);
}
}
vis[u]=(result==true?1:-1);
ans-=V[fa[u]]/2;
if(result==true) V[fa[u]]++;
ans+=V[fa[u]]/2;
return ans;
}