一、题目描述(题目链接)
给定一个序列,按指定的顺序逐一删掉,求连续子序列和的最大值。例如序列1 3 2 5,按3 4 1 2的顺序删除,即依次删除第3个、第4个、第1个、第2个,答案为5 4 3 0。
二、问题分析
我们知道从并查集中删除元素很难,而合并非常简单。所以我们可以反过来思考,正向删除元素等同于反向添加元素,将结果存起来反向输出即可。每次添加一个元素,更新最大值。很明显新加入的点只影响相邻元素的值。每添加一个元素有4种情况:单独成集合,只与前面的成集合,只与后面的成集合,既与前面的成集合又与后面的成集合。
三、代码实现
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdbool> 5 #include<algorithm> 6 using namespace std; 7 8 typedef long long LL; 9 const int maxn = 100000 + 10; 10 int n, A[maxn], B[maxn],fa[maxn]; 11 bool vis[maxn]; 12 LL res[maxn],sum[maxn]; 13 14 void init() 15 { 16 for (int i = 1; i <= n; i++) 17 { 18 sum[i] = A[i]; //sum[i]表示以i为根节点的集合的和 19 fa[i] = i; 20 } 21 } 22 23 int findset(int x) 24 { 25 if (x != fa[x]) 26 return fa[x] = findset(fa[x]); 27 return fa[x]; 28 } 29 30 void unite(int x, int y) 31 { 32 int rx = findset(x); 33 int ry = findset(y); 34 fa[rx] = ry; 35 sum[ry] += sum[rx]; //和也要合并 36 } 37 38 int main() 39 { 40 scanf("%d", &n); 41 for (int i = 1; i <= n; i++) 42 scanf("%d", &A[i]); 43 for (int i = 1; i <= n; i++) 44 scanf("%d", &B[i]); 45 46 init(); 47 memset(vis, false, sizeof(vis)); 48 int cnt = 0; 49 LL maxx = 0; 50 for (int i = n; i >= 1; i--) 51 { 52 res[cnt++] = maxx; 53 int tmp = B[i]; 54 if (tmp > 1 && vis[tmp - 1]) unite(tmp, tmp - 1); //是否与前面相邻 55 if (tmp < n && vis[tmp + 1]) unite(tmp, tmp + 1); //是否与后面相邻 56 vis[tmp] = true; 57 maxx = max(maxx, sum[findset(tmp)]); //包括了单独成集合的情况 58 } 59 for (int i = cnt - 1; i >= 0; i--) 60 printf("%lld ", res[i]); 61 return 0; 62 }