「JSOI2011」汇总

Posted zsbzsb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「JSOI2011」汇总相关的知识,希望对你有一定的参考价值。

「JSOI2011」柠檬

斜率优化题。
在优化前,还有一个值得一提的优化:
对于最后的最有分割方案,每一段的两个端点一定是同颜色的,并且作为这一段的 (s_0)
证明:如果不作为这一段的 (s_0),那么它显然没有贡献,把这一个单独分出来显然更优,直到最后两个端点就一定都是 (s_0) ,颜色相同。
那么我们只需要从之前和该点种类相同的位置进行转移即可。
这样就从直接枚举的复杂度 (O(n^3)) 优化到了 (O(n^2)) ,但还是不够,继续考虑优化。
我们先把转移方程写出来:
(dp_i) 表示把前 (i) 个取完,且 (i) 点作为一段的终点最大收益。
[dp_i=maxlimits_{1le j le i,a_j=a_i}left{dp_{j-1}+s_i(p_i-p_j+1)^2 ight}]
(p_i) 表示第 (i) 个点是种类为 (s_i) 的第 (p_i) 个点。
根据斜率优化的一些做法,我们可以把式子化成这样:
(p_i imes 2p_js_j+dp_i-s_i(p_i+1)^2=dp_{j-1}-2a_jp_j+a_jp_j^2)
(x_i = 2s_ip_i,y_i=dp_{i-1}-2s_ip_i+s_ip_i^2)
(p_ix_j+dp_i-s_i(p_i+1)^2=y_j)
因为要让 (dp_i) 最大化,所以我们对每一种颜色都用单调栈维护一个上凸包,这样才能满足决策单调性。
注意一点细节:
因为我们的转移点 (j) 的范围是 ([1,i]) 的,而我们再插入 (j) 这个点时只有 (dp_{j-1}) 这个信息,为了能取到 (dp_{i-1}) ,我们需要在寻找最优转移点之前就把 (i) 加入单调栈。
参考代码:

#include <cstdio>
#include <vector>
#define rg register
#define int long long
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 1e5 + 5;

int n, s[_], p[_], pos[_], dp[_];
vector < int > stk[_];

inline int X(int i) { return 2 * s[i] * p[i]; }

inline int Y(int i) { return dp[i - 1] - 2 * s[i] * p[i] + s[i] * p[i] * p[i]; }

inline double slope(int i, int j) { return (double) (Y(i) - Y(j)) / (X(i) - X(j)); }

signed main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n);
    for (rg int i = 1; i <= n; ++i) read(s[i]), p[i] = ++pos[s[i]];
#define A stk[c][stk[c].size() - 2]
#define B stk[c][stk[c].size() - 1]
    for (rg int i = 1; i <= n; ++i) {
        int c = s[i];
        while (stk[c].size() > 1 && slope(A, B) < slope(A, i)) stk[c].pop_back();
        stk[c].push_back(i);
        while (stk[c].size() > 1 && slope(A, B) < p[i]) stk[c].pop_back();
        int j = stk[c].back();
        dp[i] = dp[j - 1] + s[i] * (p[i] - p[j] + 1) * (p[i] - p[j] + 1);
    }
#undef A
#undef B    
    printf("%lld
", dp[n]);
    return 0;
}

「JSOI2011」分特产

传送门
计数题。
考虑容斥掉每人至少一个的限制。
就直接枚举至少有多少人没有分到特产,然后剩下的随便分。
[Ans = sum_{i = 0}^n (-1)^i {n choose i} prod_{j = 1}^m {n - i + a_j - 1 choose n - i - 1}]
参考代码:

#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 5010, p = 1e9 + 7;

int n, m, a[_], fac[_], ifc[_];

inline int C(int N, int M) { return 1ll * fac[N] * ifc[M] % p * ifc[N - M] % p; }

inline void init(int N) {
    fac[0] = ifc[0] = ifc[1] = 1;
    for (rg int i = 1; i <= N; ++i) fac[i] = 1ll * fac[i - 1] * i % p;
    for (rg int i = 2; i <= N; ++i) ifc[i] = 1ll * (p - p / i) * ifc[p % i] % p;
    for (rg int i = 1; i <= N; ++i) ifc[i] = 1ll * ifc[i - 1] * ifc[i] % p;
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n), read(m), init(_ - 1);
    for (rg int i = 1; i <= m; ++i) read(a[i]);
    int ans = 0;
    for (rg int i = 0; i <= n; ++i) {
        int tmp = 1;
        for (rg int j = 1; j <= m; ++j) tmp = 1ll * tmp * C(n - i - 1 + a[j], n - i - 1) % p;
        ans = (ans + 1ll * (i & 1 ? -1 : 1) * C(n, i) * tmp % p + p) % p;
    }
    printf("%d
", ans);
    return 0;
}

以上是关于「JSOI2011」汇总的主要内容,如果未能解决你的问题,请参考以下文章

「JSOI2011」任务调度

bzoj2209 [ JSOI2011 ] -- splay

bzoj 4709 [ Jsoi 2011 ] 柠檬 ——斜率优化DP

bzoj4710 [Jsoi2011]分特产(容斥)

BZOJ2329/2209[HNOI2011]括号修复/[Jsoi2011]括号序列 Splay

[Bzoj5179][Jsoi2011]任务调度(左偏树)