[并查集][线段树]luogu P3273 [SCOI2011]棘手的操作
作者:互联网
题面
https://www.luogu.com.cn/problem/P3273
大意是给n个初始独立的点,有7种操作:
1、在两个点之间连一条无向边
2、给一个点加上一个权值
3、给一个点所在的连通块中的所有点加上一个权值
4、给所有点加上一个权值
5、查询一个点的权值
6、查询一个点所在的连通块中的所有点的最大权值
7、查询所有点的最大权值
分析
查询和修改操作都很像线段树,但是由于连边操作,序号是断裂的
考虑离线用链表来维护点的线段树序号,当连接两个连通块时,将两个连通块的序号序列用链表相连,最后按照链表上的顺序给点编号
并查集维护连通块,这样重标号的序列每次合并后都是一个连续的区间了
再将所有操作做一遍,连边操作就将区间端点扩张一下即可,剩下就是线段树基操了
代码
#include <iostream> #include <cstdio> #include <cstring> #define lson (x<<1) #define rson ((x<<1)+1) using namespace std; const int Inf=2147483647; const int N=3e5+10; struct Task { int tp,x,y; }p[N]; int n,m; int f[N],t[4*N],lz[4*N],ref[N],bac[N],cnt,l[N],r[N],pre[N],nex[N],st[N],ed[N],a[N]; int Get_F(int x) {return f[x]==x?x:f[x]=Get_F(f[x]);} void Pushdown(int x,int l,int r) { if (!lz[x]) return; int mid=l+r>>1; t[lson]+=lz[x];lz[lson]+=lz[x]; t[rson]+=lz[x];lz[rson]+=lz[x]; lz[x]=0; } void Change(int x,int l,int r,int ll,int rr,int k) { if (ll<=l&&r<=rr) {t[x]+=k;lz[x]+=k;return;} int mid=l+r>>1; Pushdown(x,l,r); if (ll<=mid) Change(lson,l,mid,ll,rr,k); if (mid<rr) Change(rson,mid+1,r,ll,rr,k); t[x]=max(t[lson],t[rson]); } int Query(int x,int l,int r,int ll,int rr) { if (ll<=l&&r<=rr) return t[x]; int mid=l+r>>1,ans=-Inf; Pushdown(x,l,r); if (ll<=mid) ans=Query(lson,l,mid,ll,rr); if (mid<rr) ans=max(ans,Query(rson,mid+1,r,ll,rr)); return ans; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=st[i]=ed[i]=i; scanf("%d",&m); for (int i=1,x,y;i<=m;i++) { char s[4]; scanf("%s",s); if (s[0]=='U') { scanf("%d%d",&x,&y); x=Get_F(x);y=Get_F(y); if (x!=y) f[y]=x,pre[st[y]]=ed[x],nex[ed[x]]=st[y],ed[x]=ed[y]; p[i]=(Task){0,x,y}; } if (s[0]=='A') { scanf("%d",&x); if (s[1]<'3') scanf("%d",&y); p[i]=(Task){s[1]-'0',x,y}; } if (s[0]=='F') { if (s[1]<'3') scanf("%d",&x); p[i]=(Task){s[1]-'0'+3,x,y}; } } for (int i=1;i<=n;f[i]=i,i++) if (!pre[i]) for (int j=i;j;j=nex[j]) bac[l[j]=r[j]=ref[j]=++cnt]=j,Change(1,1,n,ref[j],ref[j],a[j]); for (int i=1,x,y;i<=m;i++) { x=p[i].x;y=p[i].y; if (!p[i].tp) { x=Get_F(x);y=Get_F(y); if (x!=y) f[y]=x,l[x]=min(l[x],l[y]),r[x]=max(r[x],r[y]); } if (p[i].tp==1) Change(1,1,n,ref[x],ref[x],y); if (p[i].tp==2) x=Get_F(x),Change(1,1,n,l[x],r[x],y); if (p[i].tp==3) Change(1,1,n,1,n,x); if (p[i].tp==4) printf("%d\n",Query(1,1,n,ref[x],ref[x])); if (p[i].tp==5) x=Get_F(x),printf("%d\n",Query(1,1,n,l[x],r[x])); if (p[i].tp==6) printf("%d\n",t[1]); } }View Code
标签:连通,P3273,int,luogu,ll,查集,权值,lz,线段 来源: https://www.cnblogs.com/mastervan/p/14553201.html