CF1307E - Cow and Treats
题意
N
N
N颗草,每颗草有甜度
s
i
s_i
si,
M
M
M头牛,每头牛有最喜欢的甜度
f
i
f_i
fi和饥饿度
h
i
h_i
hi
将牛分出不相交的两个子集,一个在草的左边,一个在草的右边
两遍的牛轮流去吃草(左边的牛从左往右吃,右边的从右往左吃),每头牛只会吃最喜欢的甜度
f
i
f_i
fi的草,直到吃了
h
i
h_i
hi份的草,牛吃了的草不会再长出来
此时牛会停下来睡觉,使得后来的左右两边的牛都无法经过他
如果牛吃到
h
i
h_i
hi份草,那么他就会高兴,反之不高兴
现在求出有多少种不想交的两个子集能使最多的牛高兴
题解
这里我就不说甜度
s
s
s了,看做是颜色
c
c
c
先考虑左边的牛,显然,一种颜色只能吃一遍,因为吃过一遍后,后面吃同一种颜色的牛会发现这种草都被吃完了,而且不能越过前面的牛
右边的牛同理,所以一种颜色只能有三种情况,
0
,
1
,
2
0,1,2
0,1,2头牛吃
所以我们以第
i
i
i颗草为分分界线
记当前这种颜色
c
[
i
]
c[i]
c[i]为
x
x
x
[
1
,
i
]
[1,i]
[1,i]中颜色为
x
x
x的数量为
l
s
u
m
[
x
]
=
∑
j
=
1
i
(
c
[
j
]
=
=
x
)
lsum[x]=\displaystyle\sum_{j=1}^i(c[j]==x)
lsum[x]=j=1∑i(c[j]==x)
[
i
+
1
,
n
]
[i+1,n]
[i+1,n]中颜色为
x
x
x的数量为
r
s
u
m
[
x
]
=
∑
j
=
i
+
1
n
(
c
[
j
]
=
=
x
)
rsum[x]=\displaystyle\sum_{j=i+1}^n(c[j]==x)
rsum[x]=j=i+1∑n(c[j]==x)
现在我们求左边的牛就吃
l
s
u
m
lsum
lsum颗草,右边吃至多
r
s
u
m
rsum
rsum颗草的种数
c
n
t
1
=
n
u
m
[
x
]
[
l
s
u
m
]
cnt1= num[x][lsum]
cnt1=num[x][lsum]为左边可选数,
c
n
t
2
=
∑
j
=
1
r
s
u
m
n
u
m
[
x
]
[
j
]
cnt2=\displaystyle\sum_{j=1}^{rsum} num[x][j]
cnt2=j=1∑rsumnum[x][j]为右边可选量
这里不方便算
c
n
t
2
cnt2
cnt2,所以
n
u
m
num
num可以记为前缀和
则
c
n
t
1
=
n
u
m
[
x
]
[
l
s
u
m
]
−
n
u
m
[
x
]
[
l
s
u
m
−
1
]
,
c
n
t
2
=
n
u
m
[
x
]
[
r
s
u
m
]
cnt1=num[x][lsum]-num[x][lsum-1],cnt2=num[x][rsum]
cnt1=num[x][lsum]−num[x][lsum−1],cnt2=num[x][rsum]
此外因为两个集合是不相交的,所以当
l
s
u
m
≤
r
s
u
m
lsum\leq rsum
lsum≤rsum时
c
n
t
2
−
−
cnt2--
cnt2−−
这样颜色
x
x
x的种数就是
c
n
t
1
∗
c
n
t
2
cnt1*cnt2
cnt1∗cnt2
然后看看别的颜色
y
y
y
左边至多选
l
s
u
m
[
y
]
=
∑
j
=
1
i
(
c
[
j
]
=
=
y
)
lsum[y]=\displaystyle\sum_{j=1}^i(c[j]==y)
lsum[y]=j=1∑i(c[j]==y)
右边至多选
r
s
u
m
[
y
]
=
∑
j
=
i
+
1
n
(
c
[
j
]
=
=
y
)
rsum[y]=\displaystyle\sum_{j=i+1}^n(c[j]==y)
rsum[y]=j=i+1∑n(c[j]==y)
c
n
t
1
=
n
u
m
[
y
]
[
l
s
u
m
]
,
c
n
t
2
=
n
u
m
[
y
]
[
r
s
u
m
]
cnt1=num[y][lsum],cnt2=num[y][rsum]
cnt1=num[y][lsum],cnt2=num[y][rsum]
再和上面一样考虑一下重复就行
代码
#include <bits/stdc++.h>
#define sz sizeof
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
const int MAX = 5e3 + 10;
const ll mod = 1e9 + 7;
int N, M;
int c[MAX];
ll lsum[MAX], rsum[MAX], num[MAX][MAX];
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++) {
scanf("%d", &c[i]);
rsum[c[i]]++;
}
for (int i = 1; i <= M; i++) {
int x, y; scanf("%d%d", &x, &y);
for (int j = y; j <= N; j++) num[x][j]++;
}
pll ans = make_pair(0, 0);
//以i为分界线
for (int i = 0; i <= N; i++) {//i = 0的情况就是只有一边
if (i) lsum[c[i]]++, rsum[c[i]]--;//维护lsum和rsum
int x = c[i];
ll ls = lsum[x], rs = rsum[x];
pll t = make_pair(0, 1);
if (i) {//考虑当前这种颜色
ll cnt1 = num[x][ls] - num[x][ls - 1], cnt2 = num[x][rs] - (ls <= rs);
if (!cnt1) continue;
t.first++, t.second = t.second * cnt1 % mod;
if (cnt2) t.first++, t.second = t.second * cnt2 % mod;
}
for (int y = 1; y <= N; y++)
if (y != x) {//别的颜色
ll cnt1 = num[y][lsum[y]], cnt2 = num[y][rsum[y]];
if (cnt1 > cnt2) swap(cnt1, cnt2);//cnt1 <= cnt2
if (cnt2) {
t.first++;
//t.second *= cnt1 * (cnt2 - 1) 分类讨论
if (!cnt1) t.second = t.second * cnt2 % mod;
else if (cnt2 == 1) t.second = 2ll * t.second % mod;//只有一个,两种: 放左或者放右
else t.first++, t.second = t.second * cnt1 * (cnt2 - 1) % mod;
}
}
if (t.first > ans.first) ans = t;
else if (t.first == ans.first) ans.second = (ans.second + t.second) % mod;
}
printf("%lld %lld\n", ans.first, ans.second);
return 0;
}