[NOIP2025] 清仓甩卖 / sale
我常常思考定价。
然而我们按原价从大到小排序。
那现在考虑怎么样才会取不到最优解。
- 还剩 \(2\) 元钱,买了一个 \(1\) 元的糖果 \(x\),最后看到一个 \(2\) 元的糖果 \(y\) 但是你买不了了。
这个时候因为你不是最优解所以 \(a_x < a_y\)。
然后因为你先选了 \(x\) 所以 \(a_x > \dfrac{a_y}{2}\)。
这个时候你枚举 \(x\) 和 \(y\)。
你发现性价比比 \(y\) 高的 \(2\) 元糖果和性价比比 \(x\) 高的 \(1\) 元糖果都得选。
准确来说就是 \(y\) 之前的 \(2\) 元糖果和 \(1\) 之前的 \(1\) 元糖果都得选。
所以你选了 \(1\) 到 \(y-1\) 的所有糖果,还有 \(y+1\) 到 \(x-1\) 的所有 \(1\) 元糖果。
然后你发现最优解会再把 \(y\) 选上,贪心解会再把 \(x\) 选上。
你考虑进行计数。假设 \(1\) 到 \(y-1\) 有 \(i\) 个 \(2\)。
则 \(1\) 到 \(y-1\) 的总定价是 \(1 \times (y-1-i) + 2 \times i = y - 1 + i\)。
然后你一共 \(m\) 元钱,除掉买 \(x\) 或者买 \(y\) 的钱还剩 \(m-2\) 元。
那你在 \(y+1\) 到 \(x-1\) 里面 \(1\) 的个数就是 \((m-2)-(y-1+i)=m-y-i-1\)。
那 \(1\) 到 \(y-1\) 有 \(y-1\) 个数,\(y+1\) 到 \(x-1\) 有 \(x-y-1\) 个数对吧。
所以就是 \(\sum\limits_{i} \dbinom{y-1}{i}\dbinom{x-y-1}{m-y-i-1}=\dbinom{x-2}{m-y-1}\) 对吧。
诶那别的东西怎么定价啊。
你考虑 \(1\) 到 \(x\) 的定价都已经确定了。这个时候考虑 \(x+1\) 到 \(n\) 的定价。
如果全是 \(2\),那其实就是上面的故事。
如果有 \(1\) 的话,那你其实还能买,所以就有下面的情况。
- 还剩 \(2\) 元钱,买了一个 \(1\) 元的糖果 \(x\),然后看到一个 \(2\) 元的糖果 \(y\) 但是你买不了了,最后你又买了一个 \(1\) 元的糖果 \(z\)。
显然 \(a_x + a_z < a_y\),然后又有 \(a_x > \dfrac{a_y}{2}\),显然可以推出 \(a_z < \dfrac{a_y}{2}\)。
你还是枚举 \(x\) 和 \(y\),然后你记 \(p\) 是满足上面条件的最小的 \(z\),则 \(z\) 在 \(p\) 到 \(n\) 之间取。
此时 \(y\) 之前的 \(2\) 元糖果和 \(x\) 之前的 \(1\) 元糖果都得选。
最优解会再选 \(y\),贪心解会选 \(x\) 和 \(z\)。
你考虑 \(1\) 到 \(y-1\) 里面的所有糖果都会选,还是设里面有 \(i\) 颗 \(2\) 元糖果。
所以前面用了 \(y-1+i\) 元对吧
然后你还剩 \(m-y-i-1\) 元来买 \(y+1\) 到 \(x-1\) 里面的 \(1\) 元糖果。
那你 \(1\) 到 \(y-1\) 有 \(y-1\) 个数,\(y+1\) 到 \(x-1\) 有 \(x-y-1\) 个数。
所以还是上面的式子吗?
考虑此时 \(x+1\) 到 \(z-1\) 的糖果,容易发现定价一定为 \(2\) 元,否则就会将 \(z\) 换掉。
但是 \(z+1\) 到 \(n\) 的糖果就不一样。他们其实是随便选的。
所以是 \(\sum\limits_{z=p}^{n}\sum\limits_{i} 2^{n-z} \dbinom{y-1}{i}\dbinom{x-y-1}{m-y-i-1}=\sum\limits_{z=p}^{n} 2^{n-z} \dbinom{x-2}{m-y-1}\)。
然后你稍微提一下就是 \((2^{n-p+1}-1) \dbinom{x-2}{m-y-1}\)。
带上第一种情况,就是 \(2^{n-p+1} \dbinom{x-2}{m-y-1}\)。
这个 \(p\) 显然可以指针维护对吧。
那你不是 \(O(n^2)\) 做完了吗。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=998244353;
const int N=5000,V=10000;
int dp[V+10][V+10],pow_2[V+10];
void init(){
dp[0][0]=1;
pow_2[0]=1;
for(int i=1;i<=V;i++){
pow_2[i]=pow_2[i-1]*2%mod;
dp[i][0]=1;
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;
}
}
}
int a[N+10];
int C(int num1,int num2){
if(num2<0 || num2>num1){
return 0;
}
else{
return dp[num1][num2];
}
}
int main(){
init();
int test_id,test_tot;
scanf("%d %d",&test_id,&test_tot);
while(test_tot--){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
reverse(a+1,a+n+1);
int ans=pow_2[n];
for(int y=1;y<=n;y++){
int p=n+1;
for(int x=y+1;x<=n;x++){
while(p-1>x && a[x]+a[p-1]<a[y]){
p--;
}
if(a[x]<a[y] && a[y]<a[x]*2){
ans-=(long long)pow_2[n-p+1]*C(x-2,m-y-1)%mod;
ans=(ans%mod+mod)%mod;
}
}
}
printf("%d\n",ans);
}
return 0;
}
真搞不懂自己是怎么做到看了一个月的题解都看不懂的。
过了,无语了,气笑了。

浙公网安备 33010602011771号