题意:
现有(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;
}