地中海气候

题意

有一个包含 N 个正整数的序列,序列元素不大于 N。紧接着,他们维护了一
个可重集合 S,包含序列中的前 P 个元素。Alice 先手,二人轮流进行下面的一
系列操作:

  1. 从集合 S 中拿走一个元素,加到玩家的总分上面;
  2. 将序列中的下一个数字(如果存在)添加到集合 S 中;
    假使二人都尽力使自己的总分最大,设游戏的结果为 Alice 的总分与 Bob 的
    总分之差,那么请你在给定序列和集合元素的情况下,输出游戏的结果
    N<=100000,Q<=2000

题解

为了简单,我们可以看做本来只有P-1个元素,然后是先插入,再取出
比较显然的性质是,每次都是取的multiset中最大那个元素
于是我们就直接上个multiset或者优先队列,成功得到再nlogn的时间内搞定每次询问
那么现在的问题是怎么优化呢
我们必须优化到O(n)内完成每次查询
必须发现的一点是,每次我们只会往这个multiset中插一个值,而且如果这个值大于了当前的最大值,就一定会被马上取走
那么也就是说,这个最大值一定是不增的
那么如果当前增加值是大于这个最大值,就一定给当前的人加上这么多分
那如果不是呢?我们会取出最大值,然后插入当前的这个值。
那么问题来了怎么实现呢?
二分链表?或者干脆直接上个二叉堆?似乎那个log怎么都砍不掉啊?
然而…考虑到所有数都小于等于n,我们可以直接用一个数组来维护出现次数…成功实现O(1)插入,然后更新最大值暴力更新即可,由于是单调不增的,可以证明是O(n)的
于是最后O(NQ)解决本题
记得开ll

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N=100005;
int n,m;
int a[N],cnt[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        int p;
        scanf("%d",&p);
        int mx=0;
        for(int j=1;j<=p-1;j++){
            cnt[a[j]]++;
            mx=max(mx,a[j]);
        }
        ll ans=0;
        for(int j=1,k=1;j<=n;j++,k*=-1){
            if(p+j-1<=n){
                if(a[p+j-1]>mx)
                    ans+=k*a[p+j-1];
                else{
                    ans+=k*mx;
                    cnt[a[p+j-1]]++;
                    cnt[mx]--;
                    while(mx>0&&!cnt[mx])
                        mx--;
                }
            }
            else{
                ans+=k*mx;
                cnt[mx]--;
                while(mx>0&&!cnt[mx])
                    mx--;
            }
        }
        printf("%lld\n",ans);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值