题解:P14636 [NOIP2025] 清仓甩卖 / sale(民间数据)
首先对题意进行一下扫盲:
并不是指对于任意定价方案能达到的原价总和最大值。
而是指对于某个给定的定价方案,它利用小 R 的策略买糖果时,能否达到对于这个给定的定价方案的原价总和最大值。
大家一定要好好看样例解释啊!
然鹅我就是因为没看,被hack了十五分钟。
那么我们思考一下,在什么情况下,这个按照性价比降序排序的策略不最优。
看一下下面这个例子:
m=5
u_i 6 5 10 8 2
w_i 1 1 2 2 1
我们用
我们模拟一下这个策略,先对
那么首先会购买前三个糖果,然后因为钱不够,不能买第四个糖果,只能跳过这个,最后买第五个糖果。
那么原价为
但是我们可以不选第二个和第五个糖果,而购买第四个糖果。
那么原价为
这启发我们,这个策略可能在某些情况下买两个
观察这个策略买的糖果的位置,可以发现:
买的糖果的区间可以表示为
究其根本,是因为在买了第
记在
那么如果
于是这道题考虑反着做,计算所有使策略不优的定价方案数,也就是存在三元组
我们将原数组
观察
- 规定
a<c ,则a 是倒数第二个买的w_i=1 的糖果,而c 是最后一个买的糖果,b 是第一个被跳过的w_i=2 的糖果,毕竟是找出一组u_a+u_c<u_b ,那么左边的值越小越好,右边的值越大越好。注意a 不一定是倒数第二个买的糖果。 -
-
-
先考虑枚举
现在是让小 R 在买完糖果
分类讨论
可以画图理解一下,明白一点:
中的大小关系,与在
如果实在不明白,再看看性质
根据上述分讨,可以先让
然后在
现在加上
将
则将
其中,
预处理
代码如下:
#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";
}
}