0%

2026 杭电春季联赛 3——1007-扑克牌(在环上跑这个滑动窗口)

题目大意

image

题目背景与目标
nn 张牌从上至下组成的牌堆,每张牌上有一个点数 aia_i。共有 mm 名玩家按 1,2,,m1, 2, \dots, m 的顺序依次循环摸牌。玩家 kk 作为游戏组织者,在所有人开始摸牌之前,可以进行至多一次切牌操作(即将牌堆从任意位置分为上下两部分,并交换这两部分的位置)。目标是求出玩家 kk 通过合理切牌,最终自己摸到的牌的点数总和的最大值。

输入格式
第一行包含一个整数 TT,表示测试用例的数量。
每组数据包含两行:
第一行输入三个正整数 n,m,kn, m, k,分别代表牌的总数、玩家的总数以及目标玩家的顺位。
第二行输入 nn 个整数 a1,a2,,ana_1, a_2, \dots, a_n,自顶向下给出牌堆中每张牌的点数。

输出格式
对于每组数据,输出一行一个整数,表示玩家 kk 可以获得的最大点数和。

样例输入

1
2
3
4
5
2
2 2 1
114514 1919810
3 1 1
1 -3 2

样例输出

1
2
1919810
0

样例解释第一组样例:牌堆共 22 张牌,有 22 名玩家,主角是第 11 名玩家。初始牌堆点数为 114514, 1919810。主角可以通过切牌操作,将上面的第一张牌移到最下方,得到新牌堆 1919810, 114514。开始发牌后,主角作为第 11 名玩家摸走第一张牌,获得最大点数 19198101919810
第二组样例:牌堆共 33 张牌,只有 11 名玩家,主角是第 11 名玩家。无论主角在开始前如何切牌,因为只有他一个人玩,他都会摸走所有的牌。因此他获得的总点数固定为 13+2=01 - 3 + 2 = 0

思路讲解

其实这道题目的思路说起来也很简单,就是在一个环上跑这个滑动窗口。

这个切牌其实可以看作循环位移操作。

image

然后,需要注意到的就是,无论你怎么切牌,第 k 个玩家所能吃到的牌的数量是不变的(也就是代码中的 cnt_k )

因此,我们是用 2N 技巧,在这个 2N 上模拟环,进行一个滑动窗口,就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for (ll i = K - 1; i < N; i += M) {
cnt_k++;
ans += A[i];
}
for (int i = 0; i < M; ++i) {
ll lans = 0;
ll cnt_i = 0;
queue<ll> q;
for (ll j = i; j < 2 * N; j += M) {
lans += A[j];
q.push(A[j]);
if (SZ(q) == cnt_k) {
ans = max(ans, lans);
}
if (SZ(q) > cnt_k) {
lans -= q.front();
q.pop();
ans = max(ans, lans);
}
}
}

AC代码

AC
https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=8833

心路历程(WA,TLE,MLE……)