题意:给定n个数,再给定n个位置,每个位置依次代表此位置数消失,并且将整个数列分段,从中找最大子段和。
思路:倒过来做,把序列看成全部消失,然后再依次出现,用并查集判断本次出现的位置和他的左边,右边是否在一个集合,在的话将累计集合和,这里merge中需要加上求本集合的和,然后选择上一个的子段和与本次的集合和中较大的一个即为本次子段和,依次遍历整个序列即可!需要开一个标记数组,判断当前位置的左和右是否开通!
代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX(a,b) (a) > (b) ? (a) : (b)
#define MAX_N 100000
int f[MAX_N+5],
a[MAX_N+5],
seq[MAX_N+5],
visit[MAX_N+5];
long long prt[MAX_N+5],sum[MAX_N+5];
int find(int v)
{
if(f[v] != v)
f[v] = find(f[v]);
return f[v];
}
void merge(int u,int v)
{
int fu = find(u);
int fv = find(v);
if(fu != fv)
f[fv] = fu;
sum[fu] += sum[fv];//将一个集合的累加到当前点上
}
int main()
{
int i,n;
while(scanf("%d",&n)!=EOF)
{
memset(visit,0,sizeof(visit));
for(i=1;i<=n;i++)
{
f[i] = i;
scanf("%d",&a[i]);
sum[i] = a[i];
}
for(i=1;i<=n;i++)
{
scanf("%d",&seq[i]);
}
prt[n] = 0;
for(i=n;i>1;i--)
{
if(seq[i] > 1 && visit[seq[i]-1])//不在左边界,且左边可访问
{
merge(seq[i],seq[i]-1);//左合并
}
if(seq[i] < n && visit[seq[i]+1])//不在右边界,且右边可访问
{
merge(seq[i],seq[i]+1);//右合并
}
visit[seq[i]] = 1;//此点可访问
prt[i-1] = MAX(sum[seq[i]],prt[i]);//判断当前集合大,还是上一次的大
}
for(i=1;i<=n;i++)
printf("%I64d\n",prt[i]);
}
return 0;
}