题目描述
Alice和Bob又双叒叕开始玩游戏了。
有一个包含
N
N
N 个正整数的序列,序列元素不大于
N
N
N。紧接着,他们维护了一个可重集合
S
S
S,包含序列中的前
P
P
P 个元素。Alice先手,二人轮流进行下面的一系列操作:
1)从集合
S
S
S 中拿走一个元素,加到玩家的总分上面;
2)将序列中的下一个数字(如果存在)添加到集合
S
S
S 中;
这意味着游戏开始时,当第一个数从集合
S
S
S 中取出后,将序列的第
P
+
1
P+1
P+1 项加入到集合
S
S
S中,以此类推至集合
S
S
S 被取空为止。
假使二人都尽力使自己的总分最大,设游戏的结果为Alice的总分与Bob的总分之差,那么请你在给定序列和集合元素的情况下,输出游戏的结果。
输入格式
输入的第一行包含两个正整数
N
N
N 和
K
K
K。
N
N
N 表示给定序列的长度,
K
K
K 表示询问个数。
第二行包含
N
N
N 个正整数
a
i
a_i
ai表示序列的元素。
第三行包含
K
K
K 个正整数
P
i
P_i
Pi, 对应
K
K
K 次游戏,其中每次游戏
S
S
S 集合的初始元素为原序列的前
P
i
P_i
Pi项。
输出格式
输出共 K K K 行,每行一个整数,表示第 i i i 次游戏的结果。
样例输入
5 2
2 4 2 3 5
4 3
样例输出
2
6
样例解释
两场游戏的给定序列相同,但对于第一场游戏,开始的集合 S S S 为 { 2 , 4 , 2 , 3 } \{2,4,2,3\} {2,4,2,3},对于第二场游戏 S S S 是 { 2 , 4 , 2 } \{2,4,2\} {2,4,2}。
数据范围与提示
对于
10
%
10 \%
10%的数据,
1
≤
N
≤
10
1≤N≤10
1≤N≤10;
对于
30
%
30 \%
30%的数据,
1
≤
N
≤
600
1≤N≤600
1≤N≤600;
对于
50
%
50 \%
50%的数据,
1
≤
N
≤
10000
,
1
≤
K
≤
1000
1≤N≤10 000,1≤K≤1 000
1≤N≤10000,1≤K≤1000;
对于所有数据,
1
≤
N
≤
100000
,
1
≤
K
≤
m
i
n
(
2000
,
N
)
,
1
≤
a
i
,
p
i
≤
N
1≤N≤100 000,1≤K≤min(2 000,N),1≤a_i,p_i≤N
1≤N≤100000,1≤K≤min(2000,N),1≤ai,pi≤N.
题解
当一个数被加入到集合
S
S
S后,我们可以把它视作为“解锁”了。显然,Alice和Bob每次选择的都是已“解锁”的数中最大的。这样,我们就可以算出每个数在第几次选择时被“解锁”,存储在数组unlock中。
接下来,把所有的数从大到小排序,然后遍历这个数组。
对于我们首先碰到的数s[j],因为它们更大,一旦“解锁”马上就会被选走。这个时候unlock[j]就是这个数被选走时的次数。将这个次数对2取模,就能判断出是Alice还是Bob选走了这个数。特别地,对于unlock[j]=0(一开始就被解锁)的数,它的次数就是它在所有unlock[j]=0的数中的排名,这个可以一边选择一边得出。
但当我们不断遍历下去,总有一个数根据unlock[j]算出的次数已经被前面的数所占用,由于前面的数更大肯定是前面的优先,那该怎么办?
在前面的数选择过的次数间,会有一些空隙,由于s[j]仍是未安排过的数中最大的,我们可以求得这些空隙中的最前面那个并安排给它(如果没有,那就取最后一个选过的次数的后一个)。
最后,不必担心这个空隙会小于unlock[j]。如果前面有小于unlock[j]的空隙,就不可能出现现在这个数的次数已被占用的情况。(虽然我太蒟蒻不会证,不放心可以判一下,反正不会错(安详
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,k,unlock[100005],p,xbmax,que,zrsum;
long long asum,bsum;
struct aa{
int num,rk,xb;
}s[100005];
bool used[100005],flag;
bool cmp(aa x,aa y){
return x.num>y.num;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&s[i].num);
s[i].xb=i;
}
sort(s+1,s+n+1,cmp);
for(int i=1;i<=k;i++){
scanf("%d",&p);
asum=0;
bsum=0;
zrsum=0;
xbmax=0;
que=0;
used[0]=0;
for(int j=1;j<=n;j++){
unlock[j]=max(0,j-p);
used[j]=0;
}
for(int j=1;j<=n;j++){
if(!unlock[s[j].xb]){
s[j].rk=zrsum;//在unlock为0的数中的排名
zrsum++;
}
else s[j].rk=unlock[s[j].xb];
if(used[s[j].rk]==1){
flag=0;
for(int qq=que;qq<=xbmax;qq++){
if(!used[qq]){
que=qq;
flag=1;
break;
}
}
if(!flag) que=xbmax+1;//没有空缺
s[j].rk=que;
}
used[s[j].rk]=1;
xbmax=max(xbmax,s[j].rk);//到目前为止最大的次数
if(s[j].rk%2==0) asum+=s[j].num;
else bsum+=s[j].num;
}
printf("%lld\n",asum-bsum);
}
return 0;
}