【cf1307E】E. Cow and Treats(组合数学)

传送门

题意:
现有(n,nleq 5000)块草皮,每块草皮都有一个美味度(s_i)
现有(m,mleq 5000)头牛,每头牛有两个属性(f_i h_i),分别表示这头牛只吃美味度为(f_i)的草皮以及要吃(h_i)块这样的草皮。
每头牛在吃饱过后就会原地打盹,之后的牛都不能从那里跨过去。我们称一头打盹的牛为“幸福的牛”。
现在可以安排这(m)头牛从左边出发或者从右边出发,现在要使得“幸福的牛”数量最多,同时统计使得数量最多的方案数。

思路:
容易发现,现在要吃某一种美味度的草皮,从一侧出发的牛不能超过一头。
然后还有一个重要的性质:

  • 假设现在确定了边界(i),即从左边出发不能超过(i)这个位置,从右边出发不能超过(i)这个位置。那么一定存在一种方案,使得左侧/右侧的草皮能吃的都吃完。

也就是说,我们不需要考虑牛从两侧出发的顺序,即,我们对于左右两侧同一种美味度的草皮,可以单独考虑其贡献:

  • 若对于美味度为(i)的草皮,左边有(l_i)块,右边为(r_i)块,有(c_{i1})头牛吃的草皮数不超过(l_i),有(c_{i2})头牛吃的草皮数不超过(r_i),不妨(c_{i1}<c_{i2}),那么此时方案数为(c_{i1}cdot (c_{i2}-1))

就有一个暴力的算法:我们依次每个位置,假设当前枚举的美味度为(s_i),那么对于其它美味度的贡献可以直接像上述来计算,对于(s_i)来说则只有(r_{pos_{now}})种情况。若这里直接将(s_i)按照上述那样来计算,则会计算相同的情况。
这样的话时间复杂度为(O(n^2))或者(O(n^2logn)),也能够通过此题。

这里的话可以优化时间复杂度到(O(n))
我们只需要维护(tot_{asleep},tot_{ways}),那么枚举每个位置时,直接减去这种美味度的贡献即可。最后统计完答案了再加回来。这样的话就去掉了一层循环。
代码可能有些细节:

/*
 * Author:  heyuhhh
 * Created Time:  2020/2/18 15:29:15
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '
'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '
'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5, MOD = 1e9 + 7;

int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}

int inv(int x) {
    return qpow(x, MOD - 2);   
}

int n, m;
int a[N];
int f[N], h[N];
int x[N], y[N];
int l[N], r[N];
vector <int> v[N];

void run(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        ++r[a[i]];
    }
    for(int i = 1; i <= m; i++) {
        cin >> f[i] >> h[i];   
        v[f[i]].push_back(h[i]);
    }
    for(int i = 1; i <= n; i++) sort(all(v[i]));
    pii ans(0, 0);
    auto upd = [&](int x, int y) {
        if(x > ans.fi) ans = MP(x, 0);
        if(x == ans.fi) ans.se = (ans.se + y) % MOD;
    };
    auto calc = [&](int i) {
        int a = upper_bound(all(v[i]), l[i]) - v[i].begin();
        int b = upper_bound(all(v[i]), r[i]) - v[i].begin();
        if(a > b) swap(a, b);
        int cnt1 = 1ll * a * (b - 1) % MOD, cnt2 = a + b;
        if(cnt1 > 0) {
            x[i] = 2;
            y[i] = cnt1;
        } else if(cnt2 > 0) {
            x[i] = 1;
            y[i] = cnt2;
        } else {
            x[i] = 0;
            y[i] = 1;
        }
    };
    int tx = 0, ty = 1;
    for(int i = 1; i <= n; i++) {
        calc(i);
        tx += x[i];
        ty = 1ll * ty * y[i] % MOD;
    }
    upd(tx, ty);
    for(int i = 1; i <= n; i++) {
        int now = a[i];
        tx -= x[now];
        ty = 1ll * ty * inv(y[now]) % MOD;
        ++l[now], --r[now];
        if(binary_search(all(v[now]), l[now])) {
            int t = upper_bound(all(v[now]), r[now]) - v[now].begin();
            if(r[now] >= l[now]) --t;
            if(t > 0) {
                x[now] = 2;
                y[now] = t;
            } else {
                x[now] = y[now] = 1;   
            }
            int nx = tx + x[now], ny = 1ll * ty * y[now] % MOD;
            upd(nx, ny);
        }
        calc(now);
        tx += x[now];
        ty = 1ll * ty * y[now] % MOD;
    }
    cout << ans.fi << ' ' << ans.se << '
';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/12326887.html