Loading

2025.2.17的模拟赛题“三好学生” 题解

2025.2.17的模拟赛题“三好学生” 题解

首先有一个思路:枚举每个集合进行判断,用一个堆模拟投票的过程,这样有 \(\mathcal O(2^nmk\log n)\) 的复杂度。

为了多项式复杂度,我们要简化判断条件。假设选取集合大小为 \(p\),由于恰好要投票给 \(k\) 个不同的人,当 \(p<k\) 的时候我们不得不投票给集合外的人,使得情况变为复杂。

由于仅关注相对大小,给 \(k\) 个人添加相当于给 \(n-k\) 个人减少,相当于将 \(a_i\) 取反后给 \(n-k\) 个人添加。

\(p<k\) 的时候,\(n-p> n-k\) 所以我们可以反过来处理另一个“没有被选择的集合”。通过分类讨论,可以避免之前考虑的“复杂的情况”。

于是先需要考虑 \(p\ge k\) 的情况,那么一个选取集合 \(S\) 合法的充要条件为:

  • \(\min_{i\in S}\{a_i\}+k\le \max_{i\not\in S}\{a_i\}\)

  • \(\sum_{i\in S}\max\{0,\max_{j\not\in S}\{a_j\}-a_i\}\le m\times k\)

  • \(|S|\ge k\)

根据这两点,我们可以设出一个 dp 式。

由于\(\max,\min\) 这两维是不断变化的,同时存进状态难以处理,我们考虑枚举\(\max\)

\(f_{i,mn,num,sum,0/1}\) 表示考虑前 \(i\) 个数,\(\min_{i\in S}\{a_i\}=mn,|S|=num\)\(\sum_{i\in S}\max\{0,\max_{j\not\in S}\{a_j\}-a_i\}=sum\) 并最终是否存在一个没有选择的数为 \(mx\) 的方案数。

可以做到 \(\mathcal O(n^4\sum a)\) 的复杂度。

\(a\) 是无序的,并且状态与最值有关,可以考虑对 \(a\) 排序后再 dp

\(a\) 排序后,没必要再枚举最大值,因为处理的 \(i\) 时刻是最大值。通过讨论 \(i\) 选不选在 dp 的过程中确定最大值,改状态 \(sum=\sum_{i\in S}a_i\),复杂度 \(\mathcal O(n^3\sum a)\)

发现对 \(a\) 排序后受 \(mn\) 限制下的可选集合为 \([\max-m,\max]\),那么我们就没必要记录最小值了。每次重新跑一遍 \(dp\),复杂度 \(\mathcal O(n^3\sum a)\) 但常数较小。

发现 dp 剩下的两维 \(num\)\(sum\) 都是一个背包。

而背包是可删除的(注意二维背包也是)

又由于每次 dp 的区间端点单调,所以不用重新跑,而是直接双指针维护,复杂度 \(\mathcal O(n^2\sum a)\)

对于 \(p<k\) 的情况,一个不选取集合 \(S\) 合法的充要条件为

  • \(\max_{i\in S}\{a_i\}-k\le \min_{i\not\in S}\{a_i\}\)

  • \(\sum_{i\in S}\max\{0,a_i-\min_{j\not\in S}\{a_j\}\}\le m\times (n-k)\)

  • \(|S|> n-k\)

可以发现将 \(a_i\leftarrow n-a_i,k\leftarrow n-k\) 后条件与 \(p\ge k\) 的情况几乎一模一样,只是注意一下没有 \(|S|=k\) 就行了。

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=205,MOD=998244353;
int n,m,v;
int dp[NN][NN*NN],a[NN],ans;
void DP(int o){
    memset(dp,0,sizeof dp);
    dp[0][0]=1;
    int sum=0,L=1;
    for(int i=0;i<n;i++){
        sum+=a[i];
        for(int j=i-L+1;j>=1;j--)//添加 
            for(int k=sum;k>=a[i];k--)
                (dp[j][k]+=dp[j-1][k-a[i]])%=MOD;
        for(;a[L]+m<a[i+1];sum-=a[L],L++)//删除 
            for(int j=1;j<=i-L+1;j++)
                for(int k=a[L];k<=sum;k++)
                    (dp[j][k]-=dp[j-1][k-a[L]])%=MOD;
        for(int j=max(0ll,v-n+i+1+o);j<=i-L+1;j++)//统计答案 
            for(int k=max(0ll,j*a[i+1]-m*v);k<=sum;k++)
                (ans+=dp[j][k])%=MOD;
    }
    return;
}
signed main(){
    cin>>n>>m>>v;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    DP(0);
    for(int i=1;i<=n;i++)a[i]=201-a[i];
    reverse(a+1,a+1+n);v=n-v;
    DP(1); 
    cout<<(ans+1+MOD)%MOD;//加上全选的方案 
    return 0;
}
posted @ 2025-02-17 19:20  lupengheyyds  阅读(16)  评论(0)    收藏  举报