「JSOI2011」概要

「JSOI2011」レモン

最適化問題の斜面。
最適化の前に、最適化言及する価値:
最後のパーティション・スキームのほとんどは、両端の各セクションが同じ色でなければならず、この段落のように\(s_0 \)
証明:このセクションがないようであれば\(S_0 \) それは明らかにこのうち単一の分を寄与しなかった最後の2つのエンドポイントがあり、確かになるまで、明らかに優れています\(S_0 \) 同じ色。
それまで私たちは親切、その点からして同じ位置に移しました。
列挙から直接この複雑\(O(N ^ 3) \) に最適化された\(O(N ^ 2)\) まだ足りない、最適化を検討し続けています。
我々は、書かれた一次転写式:
\(dp_iが\)正面を表す\(I \)完全取り、\(I \)の最大給付期間の終わりである点。
\ [dp_i = \最大\ limits_ {1 \ルJ \ルI、a_j = a_iを} \ \左{dp_ {J-1} + S_I(P_I-p_jの+を1)^ 2 \右\} \]
\( P_Iは\)を表し\(I \)タイプのポイント\(S_I \)\(P_I \)ポイント。
いくつかの練習スロープの最適化によると、私たちは、この式に入れることができます。
\(P_I \回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 ^ 2 s_ip_i \)
\(p_ix_j + dp_i-S_I(P_I + 1)^ 2 = y_j \)
作成するように\(dp_i \) 我々は色スタックモノトーンのそれぞれに対して凸包を維持するように、最大、意思決定単調を満たすように。
なお、詳細:
我々転移点として\(J \)の範囲内([1、i]を\ \ ) 、我々は挿入\(J \)をこの点のみである場合、\(dp_ {J-1} \ を取得するためにこの情報を、(I-dp_を\。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\n", dp[n]);
    return 0;
}

「JSOI2011」サブスペシャリティ

ポータルは、
タイトルを数えます。
少なくとも1人のうちの包含-排除限界を考えてみましょう。
直接何気なく残りの点、その後、少なくとも専門に割り当てられていませんどのように多くの人々列挙する。
\ [回答= \ sum_ {iは ^ 0 =} N(-1)^ iは{N Iを選択\} \ prod_ {J = 1} ^ M {N - I + a_j - 1 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\n", ans);
    return 0;
}

おすすめ

転載: www.cnblogs.com/zsbzsb/p/12244376.html