UESTC 1593 老司机破阵 优先队列+双端链表

老司机破阵

Time Limit: 4500/1500MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)


Submit  Status
老司机的女朋友被坏人抓起来了,护女票心切的老司机心急火燎的赶到坏人藏匿女票的地点想要救出她,但是老司机的神通广大坏人们早有耳闻,等老司机赶到时已经有一个法阵摆在他的面前阻挡着他。
法阵是一条直线上的n个法力元件构成的,老司机每次可以将一个法力元件击碎,法阵的能量就是所有 连贯的元件能量和的最大值
老司机非常的自信,他有一套自己的破除法阵的方案( 虽然不见得是最佳
老司机希望能实时的关注着法阵的能量,一旦能量允许,他就破阵而入,救出女票。
忙着破阵的老司机自然没有功夫去计算他每步操作之后法阵的能量,他只能将此重任交与在座的各位大侠,请大家助他一臂之力。


Input

第一行n (1 ≤ n ≤ 100,000),为法力元件数量
第二行有n个数,为每个法力元件所含有的能量ei(0 ≤ ei ≤ 1e9)
第三行有n个数,为老司机击破法力元件的顺序


Output

输出n行,为老司机每次击破法力元件后法阵的能量。


Sample input and output

Sample Input Sample Output
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


Hint

title


Source

2017 UESTC Training for Data Structures

UESTC 1593 老司机破阵


My Solution

优先队列+双端链表
cf原题,直接贴的以前的代码
反向做,按着反的顺序把元素一个一个的添加进去,用priority_queue 维护当前最值,
用双向链表维护当前区间的状态,L[i]表示以i为端点的区间的区间左端点,
R[i]表示以i为区间端点的右端点。
对于每个点 j, 当只R[i + 1] != 0 时, 右边有区间,可以把两个链表合并成一个, 
R[i] = R[i + 1], L[i]  = i;
当只L[i - 1] != 0 时, 左边有区间,可以把两个链表合并成一个, 
R[L[i - 1]] = i, R[i] = i, L[i] = L[i - 1]
当两边都有区间时,先合并一边,再合并另一边最后得到的区间是 [ L[i - 1], R[i + 1] ];
然后根据合并来的区间得到新的区间和,push到priority_queue里,这个和必定比原来分开的子区间大,
所以原来的子区间和就留在pq里不用管了。最多维护n个元素。
区间和用 map<pair<int, int>, LL> mp,来维护, mp[ii(i, i)] = a[per[i]];, 
然后 mp[ii(l, r)]就用来维护新的区间的和,然后push到pq里。
复杂度 O(nlogn)


#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
using namespace std;
typedef long long LL;
typedef pair<int, int> ii;
const int maxn = 1e5 + 8;

priority_queue<LL> pq;
map<ii, LL> mp;

LL a[maxn], per[maxn], L[maxn], R[maxn], ans[maxn];

int main()
{
    #ifdef LOCAL
    freopen("k.txt", "r", stdin);
    //freopen("k.out", "w", stdout);
    int T = 3;
    while(T--){
    #endif // LOCAL
    //ios::sync_with_stdio(false); cin.tie(0);

    LL n, l, r, sum;
    //cin >> n;
    scanf("%lld", &n);
    for(int i = 1; i <= n; i++){
        //cin >> a[i];
        scanf("%lld", &a[i]);
    }
    for(int i = 1; i <= n; i++){
        //cin >> per[i];
        scanf("%lld", &per[i]);
    }

    for(int i = n; i > 0; i--){
        if(i == n){
            L[per[n]] = per[n];
            R[per[n]] = per[n];
            mp[ii(per[n], per[n])] = a[per[n]];
            pq.push(a[per[n]]);
            ans[n] = 0;
        }
        else{
            ans[i] = pq.top();
            sum = a[per[i]];
            l = per[i], r = per[i];
            L[l] = l, R[l] = l;
            if(L[per[i]+1] != 0){
                L[per[i]] = per[i];
                R[per[i]] = R[per[i]+1];
                L[R[per[i]+1]] = per[i];
                r = R[per[i]];
                sum += mp[ii(per[i]+1, r)];
            }
            if(R[per[i]-1] != 0){
                if(R[per[i]] != 0){
                    R[L[per[i]-1]] = R[per[i]];
                    L[R[per[i]]] = L[per[i]-1];
                    l = L[per[i]-1];
                    sum += mp[ii(l, per[i]-1)];

                }
                else{
                    R[L[per[i]-1]] = per[i];
                    R[per[i]] = per[i];
                    L[per[i]] = L[per[i]-1];
                    l = L[per[i]-1];
                    sum += mp[ii(l, per[i]-1)];
                }

            }
            mp[ii(l, r)] = sum;
            pq.push(sum);
        }
    }

    for(int i = 1; i <= n; i++){
        //cout << ans[i] << "\n";
        printf("%lld\n", ans[i]);
    }
    #ifdef LOCAL
    memset(L, 0, sizeof L);
    memset(R, 0, sizeof R);
    mp.clear();
    while(!pq.empty()) pq.pop();
    cout << endl;
    }
    #endif // LOCAL
    return 0;
}


  Thank you!

                                                                                                                                             ------from ProLights

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值