有N个节点,标号从1到N,这N个节点一开始相互不连通。第i个节点的初始权值为a[i],接下来有如下一些操作:
U x y: 加一条边,连接第x个节点和第y个节点
A1 x v: 将第x个节点的权值增加v
A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v
A3 v: 将所有节点的权值都增加v
F1 x: 输出第x个节点当前的权值
F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值
F3: 输出所有节点中,权值最大的节点的权值
有N个节点,标号从1到N,这N个节点一开始相互不连通。第i个节点的初始权值为a[i],接下来有如下一些操作:
U x y: 加一条边,连接第x个节点和第y个节点
A1 x v: 将第x个节点的权值增加v
A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v
A3 v: 将所有节点的权值都增加v
F1 x: 输出第x个节点当前的权值
F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值
F3: 输出所有节点中,权值最大的节点的权值
输入的第一行是一个整数N,代表节点个数。
接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。
再下一行输入一个整数Q,代表接下来的操作数。
最后输入Q行,每行的格式如题目描述所示。
对于操作F1, F2, F3,输出对应的结果,每个结果占一行。
对于30%的数据,保证 N<=100,Q<=10000
对于80%的数据,保证 N<=100000,Q<=100000
对于100%的数据,保证 N<=300000,Q<=300000
对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000
Day2
题解:线段树+离线操作。
这道题貌似是可以用可并堆或者堆套堆的,但是我太水了,并不会,只好用线段树来搞一搞啦。
先离线所有询问,对于所有的U操作先进性预处理,按照读入的顺序用并查集把一个连通块内的点并到一起,并不断的更新每个连通块的最后一个节点。然后按照每个连通块的顺序,把同一个连通块中的节点放到一起,然后用线段树维护。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define N 300003 #define inf 1000000000 using namespace std; int n,m; int tr[N*4],delta[N*4],maxn[N*4],a[N]; int op[N],x[N],v[N],sz,pos[N],val[N]; int fa[N],end[N],next[N]; void init() { scanf("%d",&m); for (int i=1;i<=m;i++) { char s[10]; scanf("%s",s); if (s[0]=='A') { if (s[1]=='1') op[i]=2,scanf("%d%d",&x[i],&v[i]);// 将第x个节点的权值增加v if (s[1]=='2') op[i]=3,scanf("%d%d",&x[i],&v[i]);//将第x个节点所在的连通块的所有节点的权值都增加v if (s[1]=='3') op[i]=4,scanf("%d%d",&v[i]);//将所有节点的权值都增加v } if (s[0]=='U') op[i]=1,scanf("%d%d",&x[i],&v[i]);//加一条边,连接第x个节点和第y个节点 if (s[0]=='F') { if (s[1]=='1') op[i]=5,scanf("%d",&x[i]);//输出第x个节点当前的权值 if (s[1]=='2') op[i]=6,scanf("%d",&x[i]);//输出第x个节点所在的连通块中,权值最大的节点的权值 if (s[1]=='3') op[i]=7;// 输出所有节点中,权值最大的节点的权值 } } } int find(int x) { if (fa[x]==x) return x; fa[x]=find(fa[x]); return fa[x]; } void update(int x) { tr[x]=max(tr[x<<1],tr[x<<1|1]); } void pushdown(int x) { if (!delta[x]) return; delta[x<<1]+=delta[x]; delta[x<<1|1]+=delta[x]; tr[x<<1]+=delta[x]; tr[x<<1|1]+=delta[x]; delta[x]=0; } void build(int now,int l,int r) { if (l==r) { tr[now]=val[l]; return; } int mid=(l+r)/2; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now); } void pointchange(int now,int l,int r,int x,int v) { if (l==r) { tr[now]+=v; return; } pushdown(now); int mid=(l+r)/2; if (x<=mid) pointchange(now<<1,l,mid,x,v); else pointchange(now<<1|1,mid+1,r,x,v); update(now); } int findpoint(int now,int l,int r,int x) { if (l==r) return tr[now]; pushdown(now); int mid=(l+r)/2; if (x<=mid) findpoint(now<<1,l,mid,x); else findpoint(now<<1|1,mid+1,r,x); } void query(int now,int l,int r,int ll,int rr,int x) { if (l>=ll&&r<=rr) { tr[now]+=x; delta[now]+=x; return; } pushdown(now); int mid=(l+r)/2; if (ll<=mid) query(now<<1,l,mid,ll,rr,x); if (rr>mid) query(now<<1|1,mid+1,r,ll,rr,x); update(now); } int qjmax(int now,int l,int r,int ll,int rr) { if (l>=ll&&r<=rr) return tr[now]; pushdown(now); int mid=(l+r)/2; int ans=-inf; if (ll<=mid) ans=max(ans,qjmax(now<<1,l,mid,ll,rr)); if (rr>mid) ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr)); return ans; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); init(); for (int i=1;i<=n;i++) fa[i]=i,end[i]=i; for (int i=1;i<=m;i++) if (op[i]==1) { int r1=find(x[i]),r2=find(v[i]); if (r1==r2) continue; fa[r2]=r1;//并查集的代表节点 next[end[r1]]=r2;//用next数组连通块中的点连接 end[r1]=end[r2];//记录连通块的结束节点 } for (int i=1;i<=n;i++) if (find(i)==i) { for (int j=i;j;j=next[j]) sz++,pos[j]=sz,val[sz]=a[j]; } build(1,1,n); for (int i=1;i<=n;i++) fa[i]=i,end[i]=i; for (int i=1;i<=m;i++) { if (op[i]==1) { int r1=find(x[i]),r2=find(v[i]); if (r1==r2) continue; fa[r2]=r1; end[r1]=end[r2]; } if (op[i]==2) pointchange(1,1,n,pos[x[i]],v[i]); if (op[i]==3) query(1,1,n,pos[find(x[i])],pos[end[find(x[i])]],v[i]); if (op[i]==4) query(1,1,n,1,n,v[i]); if (op[i]==5) printf("%d\n",findpoint(1,1,n,pos[x[i]])); if (op[i]==6) printf("%d\n",qjmax(1,1,n,pos[find(x[i])],pos[end[find(x[i])]])); if (op[i]==7) printf("%d\n",qjmax(1,1,n,1,n)); } }