You are given an array consisting ofn non-negative integersa1, a2, ..., an.
You are going to destroy integers in the array one by one. Thus, you are given the permutation of integers from1 ton defining the order elements of the array are destroyed.
After each element is destroyed you have to find out the segment of the array, such that it contains no destroyed elements and the sum of its elements is maximum possible. The sum of elements in the empty segment is considered to be 0.
The first line of the input contains a single integern (1 ≤ n ≤ 100 000) — the length of the array.
The second line containsn integersa1, a2, ..., an (0 ≤ ai ≤ 109).
The third line contains a permutation of integers from1 ton — the order used to destroy elements.
Printn lines. Thei-th line should contain a single integer — the maximum possible sum of elements on the segment containing no destroyed elements, after firsti operations are performed.
4
1 3 2 5
3 4 1 2
5
4
3
0
5
1 2 3 4 5
4 2 3 5 1
6
5
5
1
0
8
5 5 4 4 6 6 5 5
5 2 8 7 1 3 4 6
18
16
11
8
8
6
6
0
题目大意: 给定一个乱序的数列,每次从中删去一个元素(留下空位),问剩下的连续子段和(中间不包括被删除的元素)的最大值。
解题思路:
1、正向思维:也就是正向模拟这个过程,考虑如何实现,一个Multiset保存当前所有的连续字段和,另开一个set<pair>保存每一段闭区间,要保证每次切断一个区间,加两个新区间进去,删去一个当前的连续字段和,加上两个心得连续字段和。
2、逆向思维:如果正向进行这个过程感觉没有什么好的性质,我们来逆向思考这个问题,也就是从后往前模拟这个过程。这就变成了一个将零散的区间逐渐串联起来的过程,这就变成了一个区间划分问题,就可以采用并查集来解决啦。每次插入一个值,然后判断当前位置左右两边是否存在值,如果存在,就把左右区间的值都加到当前节点上面,也就是把这个新插入的节点视作根。不断重复这个过程,将线段连接就可以了~
AC代码:
模拟做法:
#include <bits/stdc++.h>
#define Fori(x) for(int i=0;i<x;i++)
#define Forj(x) for(int j=0;j<x;j++)
#define maxn 100005
#define inf 0x3f3f3f3f
#define ONES(x) __builtin_popcount(x)
using namespace std;
typedef long long ll ;
const double eps =1e-8;
const int mod = 1000000007;
typedef pair<int, int> P;
const double PI = acos(-1.0);
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int n,m;
int ans;
ll pre[maxn];
int pos[maxn];
int main()
{
//freopen("test.txt","r",stdin);
set<P>region;
multiset<ll>sum;
cin>>n;
for(int i = 1;i<=n;i++) { int temp; cin>>temp; pre[i] = pre[i-1] + temp; }
region.insert(P(n,1));
sum.insert(pre[n]);
for(int i = 1;i<=n;i++)
{
cin>>pos[i];
P p1 = *region.upper_bound(P(pos[i],0));
region.erase(p1);
ll tmp = pre[p1.first]-pre[p1.second-1];
sum.erase( sum.lower_bound(tmp) );
P p2 = P(pos[i]-1,p1.second);
region.insert(p2);
sum.insert(pre[p2.first]-pre[p2.second-1]);
p2 = P(p1.first,pos[i]+1);
region.insert(p2);
sum.insert(pre[p2.first]-pre[p2.second-1]);
cout << *--sum.end() << endl;
}
//cout << ans << endl;
return 0;
}
并查集做法:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
int a[maxn];
int pos[maxn];
int fa[maxn];
bool ok[maxn];
ll s[maxn];
ll ans[maxn];
int find(int u)
{
if(u!=fa[u])
fa[u] = find(fa[u]);
return fa[u];
}
int merge(int u, int v)
{
int fu = find(u),fv = find(v);
fa[fv] = fu; //将原来的树挂靠到当前节点上
s[fu] += s[fv]; //将原来的根节点上的值加到当前根节点上
}
int main()
{
//freopen("test.txt","r",stdin);
ios_base::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i = 1; i<=n; i++)
{
cin>>a[i];
}
for(int i = 1; i<=n; i++)
{
cin>>pos[i];
s[pos[i]] = a[pos[i]];
fa[i] = i;
}
ll rans = 0;
for(int i = n; i>=2; i--)
{
if(pos[i]<n && ok[pos[i]+1])//判断是否有右边的元素
{
merge(pos[i],pos[i]+1);
}
if(pos[i]>1 && ok[pos[i]-1])//判断是否有左边的元素
{
merge(pos[i],pos[i]-1);
}
rans = s[pos[i]];
ok[pos[i]] = true;
ans[i] = max(ans[i+1],rans);
}
for(int i = 2; i<=n+1; i++)
cout << ans[i] << endl;;
return 0;
}