codeforces 722c 用并查集求缺省的最大子序列

本文介绍了一种利用离线算法与逆向思维解决特定问题的方法,通过逆向恢复元素并结合并查集来高效计算最大子序列和。讨论了在线与离线算法的区别,并详细展示了使用C++实现的具体代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given an array consisting of n non-negative integers a1, a2, …, an.

You are going to destroy integers in the array one by one. Thus, you are given the permutation of integers from 1 to n 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.

Input
The first line of the input contains a single integer n (1 ≤ n ≤ 100 000) — the length of the array.

The second line contains n integers a1, a2, …, an (0 ≤ ai ≤ 109).

The third line contains a permutation of integers from 1 to n — the order used to destroy elements.

Output
Print n lines. The i-th line should contain a single integer — the maximum possible sum of elements on the segment containing no destroyed elements, after first i operations are performed.

Example
Input
4
1 3 2 5
3 4 1 2
Output
5
4
3
0
Input
5
1 2 3 4 5
4 2 3 5 1
Output
6
5
5
1
0
Input
8
5 5 4 4 6 6 5 5
5 2 8 7 1 3 4 6
Output
18
16
11
8
8
6
6
0

离线加逆向思维
什么是离线算法 引用 http://blog.youkuaiyun.com/ddppqq/article/details/12450327
一、在线算法
  在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。相对的,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必。
  因为在线算法并不知道整个的输入,所以它被迫做出的选择最后可能会被证明不是最优的,对在线算法的研究主要集中在当前环境下怎么做出选择。对相同问题的在线算法和离线算法的对比分析形成了以上观点。如果想从其他角度了解在线算法可以看一下 流算法(关注精确呈现过去的输入所使用的内存的量),动态算法(关注维护一个在线输入的结果所需要的时间复杂度)和在线机器学习。
  一个很好的展示在线算法概念的例子是加拿大旅行者问题,这个问题的目标是在一个有权图中以最小的代价到达一个目标节点,但这个有权图中有些边是不可靠的,可能已经被剔除。然而一个旅行者只有到某个边的一个端点时才能确定该边是否已经被移除了。最坏情况下,该问题会变得简单,即所有的不确定的边都被移除该问题将会变成通常的最短路径问题。
二、离线算法
  离线算法设计策略都是基于在执行算法前输入数据已知的基本假设,也就是说,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果,通常将这类具有问题完全信息前提下设计出的算法成为离线算法。
题意:给出一个子序列和删除的顺序,每次删除一个数,求剩下的缺省的序列中的最大子序列和。
正着做,每次删除后增加的子序列和区间边界都是未知的,所以不能正着做,
逆着做,每次恢复一个,每回复一个求它的连续区间,用并查集,将这个数与左右区间合并成同一个区间。然后与上一个恢复数的最大子区间比较,真是太妙了

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn= 100100;
// int num[maxn];
int shunxu[maxn];
int vis[maxn];
LL ans[maxn];
LL sum[maxn];
int f[maxn];
int n;
int find(int x)
{
    return (x==f[x])?f[x]:f[x]=find(f[x]);
}
void merge(int a,int b) 
{
    if(b<1||b>n) return;
    if(!vis[b]) return;
    int p1=find(a);
    int p2=find(b);
    if(p1!=p2)
    {
        sum[p1]=sum[p1]+sum[p2]; //合并的是区间的根的和。
        f[p2]=p1;
    }
}
int main()
{
    LL mx; //数很大。
    while(cin>>n)
    {

        for(int i=0;i<=n;i++)
            f[i]=i;
        for(int i=1;i<=n;i++)
        {
            cin>>sum[i];
        }
        for(int i=1;i<=n;i++)
            cin>>shunxu[i];
        memset(vis,0,sizeof(vis));
        mx=0;
        for(int i=n;i>0;i--)
        {
            ans[i]=mx;vis[shunxu[i]]=1;
            merge(shunxu[i],shunxu[i]-1);
            merge(shunxu[i],shunxu[i]+1);//逆推 合并的是位置
            mx=max(mx,sum[find(shunxu[i])]);//反着推 合并的是位置
        }
        for(int i=1;i<=n;i++)
        {
            cout<<ans[i]<<endl;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值