专栏文章

题解:P14636 [NOIP2025] 清仓甩卖 / sale

P14636题解参与者 23已保存评论 23

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
22 条
当前快照
1 份
快照标识符
@mimyore6
此快照首次捕获于
2025/12/01 17:43
3 个月前
此快照最后确认于
2025/12/01 17:43
3 个月前
查看原文
感觉不是很难啊。可能是我比较擅长这种东西吧。
随机写了一下通过了大样例。目前能通过洛谷数据。

solution

首先考虑这个东西和贪心的关系。
对于任意给定的定价 wi{1,2}w_i \in \{1, 2\},小 R 能买到的最大价值实际上等价于直接选取原价最大的 mm 个糖果。因此,题目等价于:有多少种方案,使得贪心策略选取的糖果总价值等于该定价方案下的 0/1 背包最优解。
按性价比 aiwi\dfrac{a_i}{w_i} 降序购买。
  • wi=1w_i=1,性价比为 aia_i
  • wi=2w_i=2,性价比为 ai2\dfrac{a_i}{2}
发现贪心策略在只有 1122 两种重量时,仅在一种情况下会得到非最优解:
  • 贪心过程中,因为剩余钱数为 11,跳过了一个性价比很高但买不起的 22 元糖果 uu
  • 随后,用这 11 元钱买了一个 11 元糖果 vv(或者没买任何东西,即 vv 不存在,av=0a_v=0)。
  • 如果此时,在已经购买的 11 元糖果集合中,存在一个糖果 zz,满足 au>az+ava_u > a_z + a_v(即把 zzvv 换成 uu 会更优),那么贪心策略就失败了。
直接计算合法方案很难,我们考虑容斥。
我们需要统计满足上述导致失败的方案数。为了不重不漏,我们枚举第一个导致失败的点 uu 和用来填补的点 vv
将所有糖果按原价 aia_i 从大到小排序。 枚举 uu 作为第一个被跳过的 22 元糖果。根据 uu,我们将其他糖果 ii 分为三类:
  • 集合 AA (i<ui < u):aiaua_i \ge a_u
    • 无论 wiw_i 是 1 还是 2,其性价比都 au2\ge \dfrac{a_u}{2}。这些糖果一定在 uu 之前被考虑。
  • 集合 BB (i>ui > u2ai>au2a_i > a_u):ai>au2a_i > \dfrac{a_u}{2}
    • wi=1w_i=1,性价比 ai>au2a_i > \dfrac{a_u}{2},在 uu 之前考虑(被购买)。
    • wi=2w_i=2,性价比 ai2<au2\dfrac{a_i}{2} < \dfrac{a_u}{2},在 uu 之后考虑。
  • 集合 CC (i>ui > u2aiau2a_i \le a_u):
    • 无论 wiw_i 是 1 还是 2,性价比都 au2\le \dfrac{a_u}{2},一定在 uu 之后考虑。
对于固定的 uuwu=2w_u=2),要使其成为因没钱被跳过的糖果,必须满足在 uu 之前被考虑并购买的糖果总花费恰好为 m1m-1,且包含 AA 中的所有糖果(花费 wiw_i),以及 BBwi=1w_i=1 的糖果(花费 11)。
NA=AN_A = |A|NB=BN_B = |B|,设 kAk_AAA 中取 w=2w=2 的个数,kBk_BBB 中取 w=1w=1 的个数。
总花费 = (NAkA)×1+kA×2+kB×1=NA+kA+kB(N_A - k_A) \times 1 + k_A \times 2 + k_B \times 1 = N_A + k_A + k_B
NA+kA+kB=m1kA+kB=m1NAN_A + k_A + k_B = m - 1 \Rightarrow k_A + k_B = m - 1 - N_A
方案数转为从 NA+NBN_A+N_B 个位置中选 m1NAm-1-N_A 个位置贡献额外权重的方案数:
(NA+NBm1NA)\binom{N_A + N_B}{m - 1 - N_A}
uu 被跳过之后,剩下的钱只有 11
BB 中剩余的糖果必然是 w=2w=2(否则会在 uu 前买),买不起,跳过。
CC 中的糖果按顺序考虑:
  • 如果是 w=2w=2,买不起,跳过。
  • 遇到的第一个 w=1w=1 的糖果即为 vv
  • 如果遍历完 CC 也没遇到 w=1w=1,则 vv 不存在。
对于固定的 uuvv,方案是坏的当且仅当:存在已经购买的一元糖果 zz 使得 az<auav a_z < a_u - a_v
其中 zz 可以来自 AAwz=1w_z=1)或 BBwz=1w_z=1)。
为了计算方便,我们计算不满足交换条件的方案,然后用总数减去。
对于所有满足 az<auava_z < a_u - a_v 的候选 zz,其 wzw_z 都必须被迫取 22(如果是 AA 中)或者不能取 11(如果是 BB 中,即被迫取 22)。
记阈值 D=auavD = a_u - a_v,设 SSABA \cup B 中满足 ai<Da_i < D 的元素集合。
要避免交换,必须强制 SS 中的所有元素都取 w=2w=2,这会固定 SS 中元素对 kA,kBk_A, k_B 的贡献:
  • ASA \cap S 中的元素必须是 22 \Rightarrow kAk_A 增加 AS|A \cap S|
  • BSB \cap S 中的元素必须是 2 \Rightarrow kBk_B 不增加(因为 kBk_B 统计的是 w=1w=1)。
剩余可以任选的:(NA+NB)S(N_A + N_B) - |S|
剩余需要的计数:(m1NA)AS(m - 1 - N_A) - |A \cap S|
方案数:
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; } ``` :::

评论

23 条评论,欢迎与作者交流。

正在加载评论...