题解:P14636 [NOIP2025] 清仓甩卖 / sale
无名之雾
·
·
题解
感觉不是很难啊。可能是我比较擅长这种东西吧。
随机写了一下通过了大样例。目前能通过洛谷数据。
solution
首先考虑这个东西和贪心的关系。
对于任意给定的定价 w_i \in \{1, 2\},小 R 能买到的最大价值实际上等价于直接选取原价最大的 m 个糖果。因此,题目等价于:有多少种方案,使得贪心策略选取的糖果总价值等于该定价方案下的 0/1 背包最优解。
按性价比 \dfrac{a_i}{w_i} 降序购买。
- 若 w_i=1,性价比为 a_i。
- 若 w_i=2,性价比为 \dfrac{a_i}{2}。
发现贪心策略在只有 1 和 2 两种重量时,仅在一种情况下会得到非最优解:
-
贪心过程中,因为剩余钱数为 1,跳过了一个性价比很高但买不起的 2 元糖果 u。
-
随后,用这 1 元钱买了一个 1 元糖果 v(或者没买任何东西,即 v 不存在,a_v=0)。
-
如果此时,在已经购买的 1 元糖果集合中,存在一个糖果 z,满足 a_u > a_z + a_v(即把 z 和 v 换成 u 会更优),那么贪心策略就失败了。
直接计算合法方案很难,我们考虑容斥。
我们需要统计满足上述导致失败的方案数。为了不重不漏,我们枚举第一个导致失败的点 u 和用来填补的点 v。
将所有糖果按原价 a_i 从大到小排序。
枚举 u 作为第一个被跳过的 2 元糖果。根据 u,我们将其他糖果 i 分为三类:
对于固定的 u(w_u=2),要使其成为因没钱被跳过的糖果,必须满足在 u 之前被考虑并购买的糖果总花费恰好为 m-1,且包含 A 中的所有糖果(花费 w_i),以及 B 中 w_i=1 的糖果(花费 1)。
设 N_A = |A|,N_B = |B|,设 k_A 为 A 中取 w=2 的个数,k_B 为 B 中取 w=1 的个数。
总花费 = (N_A - k_A) \times 1 + k_A \times 2 + k_B \times 1 = N_A + k_A + k_B。
方案数转为从 $N_A+N_B$ 个位置中选 $m-1-N_A$ 个位置贡献额外权重的方案数:
$$ \binom{N_A + N_B}{m - 1 - N_A} $$
在 $u$ 被跳过之后,剩下的钱只有 $1$。
$B$ 中剩余的糖果必然是 $w=2$(否则会在 $u$ 前买),买不起,跳过。
$C$ 中的糖果按顺序考虑:
- 如果是 $w=2$,买不起,跳过。
- 遇到的第一个 $w=1$ 的糖果即为 $v$。
- 如果遍历完 $C$ 也没遇到 $w=1$,则 $v$ 不存在。
对于固定的 $u$ 和 $v$,方案是坏的当且仅当:存在已经购买的一元糖果 $z$ 使得 $ a_z < a_u - a_v $。
其中 $z$ 可以来自 $A$($w_z=1$)或 $B$($w_z=1$)。
为了计算方便,我们计算不满足交换条件的方案,然后用总数减去。
对于所有满足 $a_z < a_u - a_v$ 的候选 $z$,其 $w_z$ 都必须被迫取 $2$(如果是 $A$ 中)或者不能取 $1$(如果是 $B$ 中,即被迫取 $2$)。
记阈值 $D = a_u - a_v$,设 $S$ 为 $A \cup B$ 中满足 $a_i < D$ 的元素集合。
要避免交换,必须强制 $S$ 中的所有元素都取 $w=2$,这会固定 $S$ 中元素对 $k_A, k_B$ 的贡献:
- $A \cap S$ 中的元素必须是 $2$ $\Rightarrow$ $k_A$ 增加 $|A \cap S|$。
- $B \cap S$ 中的元素必须是 2 $\Rightarrow$ $k_B$ 不增加(因为 $k_B$ 统计的是 $w=1$)。
剩余可以任选的:$(N_A + N_B) - |S|$。
剩余需要的计数:$(m - 1 - N_A) - |A \cap S|$。
方案数:
$$ \binom{(N_A+N_B) - |S|}{(m-1-N_A) - |
A \cap S|} $$
于是当前 $(u, v)$ 对的贡献:
$$(\binom{N_A + N_B}{m - 1 - N_A} - \binom{(N_A+N_B) - |S|}{(m-1-N_A) - |
A \cap S|} ) \times 2^c$$
其中 $c$ 表示 $v$ 之后的 $C$ 中元素可以任意取的值。
复杂度 $O(T\times n^2)$。
:::success[code]
```cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void out(int x){
if(x==0){putchar('0');return;}
int len=0,k1=x,c[10005];
if(k1<0)k1=-k1,putchar('-');
while(k1)c[len++]=k1%10+'0',k1/=10;
while(len--)putchar(c[len]);
}const int N=5e3+5,mod=998244353;
int fact[N],invf[N],pow2[N],a[N];
int addmod(int x,int y){x+=y;if(x>=mod)x-=mod;return x;}
int submod(int x,int y){x-=y;if(x<0)x+=mod;return x;}
int mulmod(int x,int y){return x*y%mod;}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=mulmod(ans,a);
b>>=1;a=mulmod(a,a);
}return ans;
}int inv(int n){return qpow(n,mod-2);}
void init(){
fact[0]=1,invf[0]=1;
for (int i=1;i<N;i++) {
fact[i]=mulmod(fact[i-1],i);
invf[i]=inv(fact[i]);
}pow2[0]=1;
for(int i=1;i<N;i++)pow2[i]=mulmod(pow2[i-1],2);
}int c(int n,int r){
if(r<0||r>n)return 0;
return mulmod(fact[n],mulmod(invf[r],invf[n-r]));
}void solve(){
int n=read(),m=read(),tot=0;
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1,greater<int>());
for(int u=1;u<=n;u++){
int idx=u+1;
while(idx<=n&&2*a[idx]>a[u])idx++;
int A=u-1,B=idx-1-u,C=n-idx+1;
int tmp=c(A+B,m-1-A);
if(tmp==0)continue;
vector<pair<int,int>>cnt;
int ptrA=1,ptrB=u+1;
while(ptrA<=u-1&&ptrB<idx) {
if(a[ptrA]>=a[ptrB]){cnt.push_back({a[ptrA],0});ptrA++;}
else{cnt.push_back({a[ptrB],1});ptrB++;}
}while(ptrA<=u-1){cnt.push_back({a[ptrA],0});ptrA++;}
while (ptrB<idx){cnt.push_back({a[ptrB],1});ptrB++;}
int ptrS=cnt.size()-1,K_A=0,K_B=0;
for(int i=0;i<=C;i++) {
int val=0;
if (i<C)val=a[idx+i];
int D=a[u]-val;
while(ptrS>=0&&cnt[ptrS].first<D) {
if(cnt[ptrS].second==0)K_A++;
else K_B++;ptrS--;
}int g=c(A+B-K_A-K_B,m-1-A-K_A),b=submod(tmp,g);
if(b>0){
int rem_C=(i<C)?(C-1-i):0,term=mulmod(b,pow2[rem_C]);
tot=addmod(tot,term);
}
}
}int ans=submod(pow2[n],tot);
cout<<ans<<"\n";
}
signed main() {
freopen("sale11.in","r",stdin);
freopen("sale.out","w",stdout);
init();int c=read(),t=read();
while(t--)solve();
return 0;
}
```
:::