题目描述
给定一个长度为 $n$ 的非负整数序列 $a_1,a_2,…,a_n$。
其中的所有元素将被逐个封印。
具体封印顺序可以用一个 $1∼n$ 的排列 $b_1,b_2,…,b_n$描述;
第 $i$ 个被封印的元素即为 $a_{bi}$ 。
你需要完成 $n$ 个任务(编号 $1∼n$),其中第 $i$ 个任务是:对于完成前 $i$ 次封印的序列,请你找到序列中的一个连续子序列(可以为空),使得该子序列不含任何被封印的元素,且子序列内各元素之和尽可能大,输出这个子序列元素和的最大可能值。
空序列的元素和视为 $0$。
输入格式
第一行包含整数 $n$。
第二行包含 $a_1,a_2,…,a_n$。
第三行包含 $b_1,b_2,…,b_n$。
输出格式
共 $n$ 行,第 $i$ 行输出第 $i$ 个任务的结果。
数据范围
前 $4$ 个测试点满足 $1\leq n\leq 10$。
所有测试点满足 $1\leq n\leq 1e5,0\leq a_i\leq 1e9,b_1∼b_n$ 是一个 $1∼n$ 的排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 输入样例1: 4 1 3 2 5 3 4 1 2 输出样例1: 5 4 3 0 输入样例2: 5 1 2 3 4 5 4 2 3 5 1 输出样例2: 6 5 5 1 0 输入样例3: 8 5 5 4 4 6 6 5 5 5 2 8 7 1 3 4 6 输出样例3: 18 16 11 8 8 6 6 0
|
解题思路
从左往右为封印一个元素,从右往左则为插入一个元素,故本体可以视为不断插入元素,求取最大子段和。
插入一个元素有三种可能,孤立/与左边相连/与右边相连,容易想到可以用并查集来维护;
这里采取的序列并查集较为特殊,每个区间以右端点为代表节点(父节点)。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> #include <cstdio> #include <cstring> #include <algorithm>
using namespace std;
const int N = 1e5 + 10; typedef long long LL;
int a[N],b[N],p[N]; LL ans[N],s[N]; bool st[N];
int n;
int find(int x){ if(p[x] != x) p[x] = find(p[x]); return p[x]; }
int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin >> n; for(int i = 1; i <= n; ++ i) cin >> a[i]; for(int i = 1; i <= n; ++ i) cin >> b[i]; for(int i = 1; i <= n; ++ i) p[i] = i; for(int i = n; i ; -- i) { int k = b[i]; st[k] = true, s[k] = a[k]; if(st[k+1]) s[find(k+1)] += s[k], p[k] = find(k+1); if(st[k-1]) s[find(k)] += s[k-1], p[k-1] = find(k); ans[i-1] = max(ans[i],s[find(k)]); } for(int i = 1; i <= n; ++ i) cout << ans[i] << endl; return 0; }
|