FANCY的勇者之路(并查集)
题目描述
勇者和魔王的战斗,从很久以前就一直延续着。而FANCY作为这一世的勇者,却整天沉迷于美少女游戏之中,所以萎靡不振的他的战斗力不断的下降。意识到了这一点的FANCY,为了完成自己的使命,变卖了自己的全部财产,招募到了很厉害的来此异世界的暗杀团队。而魔王军队的内部本社也很混乱 ,如果能抹杀掉一部分魔王的将领,那么魔王的军队将被划分为彼此不相关的小团体。
假定魔王的军队是一个数列a,a[i]代表将领i的战斗力,如果一开始将领k被杀害的话,就会分成
1~(k-1)和(k+1)~n的两个小团体,而紧接着如果将领m被杀害的话( 1 <m<k-1) 就会分成
(1~m-1)和(m+1)~k-1,(k+1)~n三个小团体。设定小团体的战斗力为该团体所包含的将领的战斗力的总和。
现在会给出暗杀团队的暗杀计划,请输出每次杀掉将领i之前,战斗力最强的小团体的战斗力。
假定魔王的军队是一个数列a,a[i]代表将领i的战斗力,如果一开始将领k被杀害的话,就会分成
1~(k-1)和(k+1)~n的两个小团体,而紧接着如果将领m被杀害的话( 1 <m<k-1) 就会分成
(1~m-1)和(m+1)~k-1,(k+1)~n三个小团体。设定小团体的战斗力为该团体所包含的将领的战斗力的总和。
现在会给出暗杀团队的暗杀计划,请输出每次杀掉将领i之前,战斗力最强的小团体的战斗力。
输入
第一行输入一个数n,代表魔王军将领的数量( n<=1e6),
第二行依次输入n个数,代表编号1-n的将领的战斗力( 0<a[i]<=1e3)
第三行依次输入n个数,代表第i次暗杀计划中将领的编号。
第二行依次输入n个数,代表编号1-n的将领的战斗力( 0<a[i]<=1e3)
第三行依次输入n个数,代表第i次暗杀计划中将领的编号。
输出
对于每个暗杀计划,在下面的一行中,输出将领i被杀掉前的战斗力最强的小团体的战斗力。
样例输入
5
1 2 3 4 5
5 3 2 4 1
样例输出
15 10 4 4 1
分析: 题目的要求是正向删除一个一个的数,求每次删除前的情况,可以看作是反向添加一个个的数来进行离线处理。
在添加一个数的时候,最大值只有两种情况,因为添加的这个数形成的新的小团体战斗力大于原本的最大值而更新,或者直接是原本的最大值。
所以在每次添加之后,只需要计算新的小团体的战斗力,小团体可以用并查集来维护,用根节点表示小团体的战斗力之和。
代码如下:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int MAXN=1e6+10; int pre[MAXN]; int op[MAXN]; int vis[MAXN]; int sum[MAXN]; int ans[MAXN]; int a[MAXN]; int Find(int x) { int h,tmp; h=x; while(x!=pre[x]) x=pre[x]; while(h!=x) { tmp=pre[h]; pre[h]=x; h=tmp; } return x; } void join(int x,int y) { int p=Find(x); int q=Find(y); if(p!=q) { pre[p]=q; sum[q]+=sum[p]; } } int main() { // freopen("1.in","r",stdin); // freopen("1.out","w",stdout); int n,m; scanf("%d",&n); int maxx=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=a[i]; pre[i]=i; } for(int i=n;i>=1;i--) scanf("%d",&op[i]); for(int i=1;i<=n;i++) { vis[op[i]]=1; if(op[i]+1<=n&&vis[op[i]+1]) join(op[i],op[i]+1); if(op[i]-1>=1&&vis[op[i]-1]) join(op[i],op[i]-1); maxx=max(maxx,sum[Find(op[i])]); ans[n-i+1]=maxx; } for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }