P9410 题解
honglan0301 · · 题解
题目分析
事实上根号分治是完全没必要的,我这里提供一个仅基于分块的时间
首先这种问题肯定想分块,朴素的区间加区间求和可以使用散块暴力加,整块懒标记的方法处理。而其实本题也可以用同样的方法:还是先对点的编号分块,之后注意到每次询问只会问到一个连通块,于是我们不完全实时地维护每个连通块的点权和,而是对每个连通块
前者可以暴力加。后者我们考虑询问时再具体求,这只需对每个连通块维护
现在的空间是
代码
感觉这种做法的代码就挺简洁好懂,非常不错。以下是核心部分代码。
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ll long long
#define B 475
int n,m,opt,u,v,w,k[200005],L[705],R[705],bh[200005],sz[200005],ns[705];
ll sum[200005],tg[705];
vector <pair<int,int>> sm[200005];
vector <int> num[200005];
signed main()
{
cin>>n>>m; for(int i=1;i<=n;i++) k[i]=(i-1)/B+1;
for(int i=1;i<=k[n];i++) L[i]=(i-1)*B+1,R[i]=min(i*B,n);
for(int i=1;i<=n;i++) bh[i]=i,sz[i]=1,sm[i].pb(mp(k[i],1)),num[i].pb(i);
while(m--)
{
cin>>opt;
if(opt==1)
{
cin>>u>>v; u=bh[u],v=bh[v]; if(u==v) continue; if(sz[u]>sz[v]) swap(u,v);
for(auto i:num[u]) bh[i]=v,num[v].pb(i); num[u].clear(); num[u].shrink_to_fit();
for(auto i:sm[u]) ns[i.fi]+=i.se; for(auto i:sm[v]) ns[i.fi]+=i.se;
sm[u].clear(); sm[v].clear(); sm[u].shrink_to_fit(); sm[v].shrink_to_fit();
for(int i=1;i<=k[n];i++) if(ns[i]) sm[v].pb(mp(i,ns[i])),ns[i]=0; sum[v]+=sum[u]; sz[v]+=sz[u];
}
else if(opt==2)
{
cin>>u>>v>>w; int uu=k[u],vv=k[v];
if(uu==vv) {for(int i=u;i<=v;i++) sum[bh[i]]+=w;}
else
{
for(int i=u;i<=R[uu];i++) sum[bh[i]]+=w; for(int i=L[vv];i<=v;i++) sum[bh[i]]+=w;
for(int i=uu+1;i<=vv-1;i++) tg[i]+=w;
}
}
else {cin>>u; u=bh[u]; ll ans=sum[u]; for(auto i:sm[u]) ans+=(ll)i.se*tg[i.fi]; cout<<ans<<endl;}
}
}