[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;
}

真搞不懂自己是怎么做到看了一个月的题解都看不懂的。

过了,无语了,气笑了。

posted @ 2025-12-24 19:29  Oken喵~  阅读(14)  评论(0)    收藏  举报