CF1307E - Cow and Treats 思维

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=1i(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+1n(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=1rsumnum[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][lsum1],cnt2=num[x][rsum]
此外因为两个集合是不相交的,所以当 l s u m ≤ r s u m lsum\leq rsum lsumrsum c n t 2 − − cnt2-- cnt2
这样颜色 x x x的种数就是 c n t 1 ∗ c n t 2 cnt1*cnt2 cnt1cnt2

然后看看别的颜色 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=1i(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+1n(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 
#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;
}

你可能感兴趣的:(Codeforces,思维)