CF888E Maximum Subsequence

原题链接

  • 题意:给一个数列和m,在数列任选若干个数,使得他们的和对m取模后最大 \(1<=n<=30\)
  • 题解:首先看到 \(n\) 非常小,考虑暴力枚举,显然纯暴力是 \(O(2^n)\) 的复杂度,过不了,那么只能是考虑别的方法,显然 \(O(2^{\frac{n}{2}})\) 是可以过的,于是想到了折半枚举,然后就是分别枚举前半段和后半段所有情况之和,为 \(a1\)\(a2\),因此,考虑如何用这两部分和并,并且不超时。显然,当左半边是 \(a1_i\),那么必然是找到一个最大的并且 \(a2_j + a1_i < m\)\(a2\) 是最优秀的,所以发现,当遍历一侧从小到大,另一侧也可以用一个指针从大到小,这样最终也是 \(O(n\times 2)\) 的复杂度。发现双指针可以相对而行, \(\because a1_i + a2_j < m\) \(a1_{i + 1} + a2_j > a1_i + a2_j\) 所以才需要 \(j\) 向左侧移动。
  • 代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const ll NN = 1e7 + 99;
const ll N = 2e6 + 99;
ll mod = 1e9 + 7;
const ll maxn = 1e7;
ll stk[N];
ll fac[N];
ll inv_fac[N];
ll q_pow(ll a, ll k) {
    ll ret = 1;
    ll x = a;
    while (k) {
        if (k & 1) (ret *= x) %= mod;
        k >>= 1;
        (x *= x) %= mod;
    }
    return ret;
}
ll inv(ll a) { return (q_pow(a, mod - 2) % mod + mod) % mod; }
void init() {
    fac[0] = 1;
    inv_fac[0] = 1;
    fac[1] = 1;
    for (ll i = 1; i < N; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv_fac[i] = inv(fac[i]);
    }
}

ll C(ll n, ll m) { return fac[n] * inv_fac[m] % mod * inv_fac[n - m] % mod; }
ll dp[N];
ll a[N];
ll a1[N];
ll a2[N];
ll len;
ll ans[N];
void solve() {
    ll n;
    cin >> n;
    ll m;cin >> m;
    mod = m;
    ll ans = 0;
    for (int i = 1; i <= n; i ++)cin >> a[i],a[i] %= m;
    int n1 = 0;
    int n2 = 0;
    for (int i = 0; i < (1 << (n/2)); i++) {
        n1++;
        for (int j = 0; j < n/2; j ++) {
            if (i >> j & 1) {
                (a1[n1] += a[j + 1])%=mod;
            }
        }
    }
    for (int i = 0; i < (1 << (n-n / 2)); i++) {
        n2++;
        for (int j = 0; j < (n - n / 2); j++) {
            if (i >> j & 1) {
                (a2[n2] += a[j + 1 + n/2]) %= mod;
            }
        }
    }
    sort(a1 + 1, a1 + 1 + n1);
    sort(a2 + 1, a2 + 1 + n2);
    for (int i = 1, j = n2; i <= n1; i ++) {
        while (a1[i] + a2[j] >= m) {
            j--;
        }
        ans = max(ans ,a1[i] + a2[j]);
    }
    cout << ans << endl;
}
signed main() {
    ios::sync_with_stdio(0);
    init();
    ll t = 1;
    while (t--) solve();
    return 0;
}
posted @ 2021-04-01 10:45  u_yan  阅读(237)  评论(0编辑  收藏  举报