专栏文章
NOIP2025 T2 题解
P14636题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimxu2xn
- 此快照首次捕获于
- 2025/12/01 17:19 3 个月前
- 此快照最后确认于
- 2025/12/01 17:19 3 个月前
场上心态崩溃,无法 debug,被这题送退役了。
这种题,要计数,显然需要先会判定一个方案是否合法。而且一般合法条件非常强,不然没办法计数。以下讨论一个方案不合法的条件。
首先可以将 数组降序排序。以下所有价格序列默认递减。
考虑把原序列 拆成两个序列 ,分别表示新定价为 和 的。为了减少特判,假定 和 后面都有无限多个 ,这样不会出现 元钱用不完的情况。
容易证明,不管是性价比策略,还是实际最优策略, 中购买的都是一段前缀。
设 分别是 序列中购买的最后一个,则有 。
如果按照性价比排序的策略买,则应该满足 ,即 都不会因为下一个性价比更高而导致策略改变。
如果是原价最大的策略,那么应该是 。
当 时,, 。由于 都是递减的, 也递减,故 取最大值等价于满足 且 。
所以,一种方案不合法,即按照性价比策略取不到最优解,等价于以下不等式:
and and ( or )
由于 递减,所以 。
如果该条件满足,则第二个不等式成立,那么第四个一定不成立,所以第三个不等式必须要成立。而如果第三个不等式成立,则第二个不等式也一定成立。
所以实际上只有第一个和第三个不等式是有用的,条件等价于
现在可以开始计数了,暴力枚举 在原序列 中对应的下标 以及 的值,则 可以直接确定,且 , 。方案数可以直接组合数,即为 。
代入 化简一下,。
所以不需要枚举 。
而注意到 的贡献只有 一项,且对于固定的 , 对应的 是一个单调递增的后缀,所以可以用双指针解决。
有一个重要的细节, 需要枚举到 ,因为需要考虑 取到 后面补的 的情况。贡献系数应该为 。
最后答案即为 减去不合法的方案数。
时间复杂度 。
~去掉头文件和组合数预处理总共就十行,不知道场上在调什么鬼。~
代码
CPP#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define pii pair<ll,ll>
#define ve vector<ll>
#define fi first
#define se second
#define pb push_back
#define mid (l+r>>1)
#define lx (x<<1)
#define rx (x<<1|1)
using namespace std;
const ll N=5008,P=998244353;
ll T,n,m,ans,a[N],pw[N],c[N][N];
void init(){
pw[0]=1;
for(ll i=1;i<N;i++)pw[i]=pw[i-1]*2%P;
for(ll i=0;i<N;i++)
for(ll j=0;j<=i;j++)
c[i][j]=(j?c[i-1][j]+c[i-1][j-1]:1)%P;
}
void slv(){
cin>>n>>m,a[n+1]=ans=0;
for(ll i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1,greater<ll>());
for(ll j=2;j<=n;j++){
ll p=j+1,s=0;
for(ll i=j+1;i<=n+1;i++)
(s+=pw[max(n-i,0ll)])%=P;
for(ll k=max(m-j,0ll)+1;k<min(j,m);k++)
if(a[k]<2*a[j]){
while(p<=n+1&&a[p]>=a[k]-a[j])
(s-=pw[max(n-p,0ll)])%=P,p++;
(ans+=c[j-2][m-k-1]*s)%=P;
}
}
cout<<(pw[n]-ans+P)%P<<'\n';
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T>>T,init();
while(T--)slv();
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...