专栏文章
题解:P14636 [NOIP2025] 清仓甩卖 / sale(民间数据)
P14636题解参与者 1已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimyjlic
- 此快照首次捕获于
- 2025/12/01 17:39 3 个月前
- 此快照最后确认于
- 2025/12/01 17:39 3 个月前
首先对题意进行一下扫盲:
并不是指对于任意定价方案能达到的原价总和最大值。
而是指对于某个给定的定价方案,它利用小 R 的策略买糖果时,能否达到对于这个给定的定价方案的原价总和最大值。
大家一定要好好看样例解释啊!
那么我们思考一下,在什么情况下,这个按照性价比降序排序的策略不最优。
看一下下面这个例子:
CPPm=5
u_i 6 5 10 8 2
w_i 1 1 2 2 1
我们用 来代替 。
我们模拟一下这个策略,先对 降序排序(已经完成)。
那么首先会购买前三个糖果,然后因为钱不够,不能买第四个糖果,只能跳过这个,最后买第五个糖果。
那么原价为 。
但是我们可以不选第二个和第五个糖果,而购买第四个糖果。
那么原价为 ,更加优秀。
这启发我们,这个策略可能在某些情况下买两个 ,而这比买一个 的劣。
观察这个策略买的糖果的位置,可以发现:
买的糖果的区间可以表示为 。
究其根本,是因为在买了第 个糖果后,只剩下 块钱,被迫放弃后面所有的 的糖果,最后在后面捞一个 的糖果,也就是第 个糖果。
记在 中买得原价最小的 的糖果下标为 , 中原价最大 的糖果下标为 。
那么如果 ,那么就可以把糖果 和 换成糖果 ,得到更优的结果。
于是这道题考虑反着做,计算所有使策略不优的定价方案数,也就是存在三元组 的定价方案个数,然后和总可能数 做差就行。
我们将原数组 进行降序排序,以及定义一个新数组 为根据 降序排序的数组。而下文中的 表示的是糖果在 的下标, 表示的是糖果在 的下标。
观察 的性质:
- 规定 ,则 是倒数第二个买的 的糖果,而 是最后一个买的糖果, 是第一个被跳过的 的糖果,毕竟是找出一组 ,那么左边的值越小越好,右边的值越大越好。注意 不一定是倒数第二个买的糖果。
- ,,可以理一下: 是因为 ,而 是因为如果 那么根据 数组的定义,小 R 就不会拿 而是拿 了。
- 到 之间不存在 的糖果。
- 。
先考虑枚举 。
现在是让小 R 在买完糖果 和一堆 的糖果后,仅剩 块钱去买糖果 。
分类讨论 中的下标 :
- ,则 。
- ,如果 ,则 ;否则 。
- ,则 。
可以画图理解一下,明白一点:
的糖果在
中的大小关系,与在 中的大小关系是一致的...
如果实在不明白,再看看性质 中关于 的描述吧。
根据上述分讨,可以先让 在 中减一个 。
然后在 中选一些位置,让 再减一个 ,使 变到 。方案数为 。
现在加上 ,只要使 就好,那么考虑从 枚举 ,找到第一个 的位置,则方案数乘上 。因为在 之后的位置的 可以为 或 ,而 之前的位置的 只能为 ,由 的性质 和 可得。
将 从 枚举,发现 单调不降,则用一个双指针维护 就可以了。
则将 数组降序排序后,最终答案为:
其中, 表示满足 的最大下标。
预处理 的幂次和组合数后,时间复杂度为 。
代码如下:
CPP#include <iostream>
#include <algorithm>
#define maxn 5005
#define int long long
#define mod 998244353
using namespace std;
int c,t,n,m,a[maxn],qw2[maxn],fac[maxn],inv[maxn];
int C(int n,int m){
if(n<0||m<0||n<m) return 0;
return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
signed main(){
qw2[0]=fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=1;i<=maxn-5;i++) qw2[i]=qw2[i-1]*2%mod;
for(int i=2;i<=maxn-5;i++) fac[i]=fac[i-1]*i%mod;
for(int i=2;i<=maxn-5;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=maxn-5;i++) inv[i]=inv[i-1]*inv[i]%mod;
cin>>c>>t;
while(t--){
int ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1),reverse(a+1,a+n+1);
for(int r=1;r<=n;r++){
int k=n;
for(int l=r-1;l>=1;l--){
if(a[r]<a[l]&&a[l]<a[r]*2){
while(a[l]>a[r]+a[k]) k--;
ans=(ans+C(r-2,m-l-1)*qw2[n-k])%mod;
}
}
}
cout<<(qw2[n]-ans+mod)%mod<<"\n";
}
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...