CF1024Eナターシャ、サーシャとプレフィックス和 - DP /数学(組み合わせの数)

フェイス質問

  CF1024E

解決

   質問は、N- $ $ A $ 1 $、$ M $ $ A $ -1構成プレフィックスと最大の全ての配列を記載することを意図していると

 $アルゴリズム(DP)$

   $ N $、$ M $ $ 2000 $少ない、DPは、見かけ上の$ $になります

  提供$のDP [I] [J] $ $ I $は$ J最大プレフィックス$ $ $ -1の配列及び構成は、$ $ 1で表され、

  $ Jシーケンス$ $の$ -1の構成は、$のI-1 $ $ $ 1、$ J $ $ -1一番のシーケンスプラス$ $ $ 1で見ることができます$ I $ $ $ 1、得られ、それは第一に$ $ I $ 1 $、$ J-1 $ -1シーケンス$ $ $ -1プラス$の弾力でも見ることができます

  これは、[I] [J] $が$ DP [I-1]〜[J] $と$ DPも、$ DPという意味から転送された[I] [J-1] $

  $ DPの$ DP [I-1]〜[J] $の寄付を考えてみましょう[I] [J] $、および任意の$ I-1 $ A $ 1 $、$ jは$の$ -1 $シーケンスのための、および1 $ 1 $への最初の終了後、最大プレフィクスと$ 1 $が追加され、数列は、$ C(iはJ、J-1 +)$、であるので、$ DP [I] [J] + = DP [I -1] [j]は+ 1 * C(I-J + 1、j)は$

  $ DP $ DP [I] [J] $の[I] [J-1] $の寄付、およびこれを考慮して同じではなく、その上、少し面倒であることを。そうDPを$ 1 $ -1 $、最大プレフィクスに一端後の最大プレフィックスと$ 0 $配列のためのため、それは、まだ$ 0 $である[I] [J] + = DP [I] [J = 1] - 1 *(C(I-J + 1、J-1) - F [I] [J-1])は、i] [j]は$ $で表される配列$ fは[Iは、$ 1 $を$ $、全ての配列J $ $ $ A $ -1プレフィックス、からなる、及び最大数は$ 0 $配列に等しいです。

  そこで質問がfの配列を見つける方法となり

  同様の$のDPの$配列を求めていると思考プロセス。$私は$ $ $ 1、$ Jシーケンス$ $ $ -1の構成は、I-1 $ $ $ 1、$ $ -1の$ J $ほとんどのシーケンス$で見ることができますバックは、これがあること(ノート$ $配列は、DPの前にありながら、バック、)$ $ 1を与えるために添加して、さらに$ $ I $ 1 $番目、$ jの-1で見ることができます $ -1 $ $シーケンス最高のバックプラス$ $ -1得ます

  $ fが$アレイに対して、明らかに$ iが、シーケンスの終わり、元の最大プレフィックスと$ 1 $または$ -1 $の後に挿入$ 0 $配列の両方に$ 1 $または$ -1 $を挿入し、その後、leqslant J $を\します最大プレフィクスとはまだ$ 0 $であるので、F $ [I] [J] = F [I-1]を[J] + F [I] [J-1] $

  状態遷移方程式は、おそらくケースです

  初期化:

  $ F [0] [I] = 1(iはleqslant Mを\ 1 \ leqslant)$、$ 0 $剰余

  $ Dpを[I] [0] = I(Iはleqslantを\ n 1 \ leqslant)$、$ 0 $剰余

  答えは$ DP [n]は[M] $です

  時間複雑$ O(NM)$

  私はまた、試験に$ DP $、$ Fの$配列を定義したが、結果の方程式が複雑ではありません、私はそれが伝達方程式を考えることだと思うどんなに、$ DP $もっと練習ああを持っています

 コード:

#include <cstdioを> 
する#include <CStringの> 
する#include <iostreamの> 
する#include <アルゴリズム>
 使用して 名前空間STDを、
typedefの長い 長いLL。
const  int型 MAXN = 2004、MOD = 998244853 ; 

INTのN、M。
DP LL [MAXN] [MAXN]、F [MAXN] [MAXN]、C [MAXN << 1 ] [MAXN << 1 ]。

ボイドのinit()
{ 
    C [ 0 ] [ 0 ] = 1LL。
    以下のためにint型私= 1 ; iがM <= N +、++I)
    { 
        C [i]が[ 0 ] = 1LL。
        INT J = 1 ; J <= I; ++ j)は
            C [I] [J] =(C [I- 1 ] [J- 1 ] + C [I- 1 ] [j])%MOD。
    } 
} 

int型のmain()
{ 
    scanf関数(" %D%dの"、&​​N、&M)。
    初期化(); 
    以下のためにint型 I = 1 ; I <= M; ++ I)
    { 
        F [ 0 ] [I] = 1 INTの J = 1 ; J <= I; ++ j)は
            F [J] [I] =(F [J- 1 ] [I] + F [j]は[I- 1 ])%MOD。
    } 
    のためにint型 i = 1 ; iは= N <; ++ I)
        DP [I] [ 0 ] = I。
    以下のためにint型 i = 1 ; iは= N <; ++ I)
         のためにint型 J = 1 ; J <= M; ++ j)は
            DP [I] [J] =(((DP [I- 1 ] [ J] + C [iがJ-+ 1を] [J] + DP [I]、[J- 1 ] - C [I + J- 1 ] [I] + F [I] [J- 1 ])%のMOD)+ MOD)%MOD。
    printf(" %LLDする\ n " 、DP [n]は[M])。
    リターン 0 ; 
}
コードの表示

 

2つのアルゴリズム(数学)

   アルゴリズムのアイデアは、非常に巧妙であるこのからあるブログ学校に

  座標系は次のようにみなされる場合$ 1 $は、ステップアップを取る$ $ -1として見右にステップを取る、出発点は、$(0,0)$、次いで$(n、m)は$、のいずれかのエンドポイントであります$配列から$開始(0、0)となり、右またはのみ$(nは、m)を行って、上がるのパスを$

  提供$ F [i]は$プレフィックス及びより大きいまたは最大数の配列に等しい私

  F次に$ [i]の点$(x、y)の数に等しい$を$パスがパスは、$ Xの$、$ yを$の存在、以下の条件を満足するよう$ I \ leqslant Xその - のy $、すなわち$ yを\ leqslant X - 私は、$ $(1 \ leqslant X \ leqslant nは、1 \ leqslantのY \のleqslantのM)$

  線形プログラミングは、それがF $を言うことである、と考え[i]は$パスに等しい介して直線の$ Y = X - Iは、パスの数とその下の領域を$

  場合M $、$ [I] = C(N + M、N)$ F - $ 1 \ leqslant iはleqslantを\ n場合のいずれかのパスはそう、$(n、m)は$を検討します

  而当$max(n - m, 1) \leqslant i \leqslant n$ 时, 我们需要把路径转化一下,起点不再是$(0, 0)$,而是$(i, -i)$,终点不变, 同样是要走$n + m$步走到$(n, m)$,因为只能向右和向上走,那么就一定会经过直线$y = x - i$ 及其下面区域, 因此此时$f[i] = C(n + m, n - i)$

  显然除了上述两种情况外的i, 都有$f[i] = 0$

  那么最终的答案为$\sum_{i = 1}^{n} i * (f[i] - f[i+1])$

  预处理阶乘与阶乘逆元,以便快速求出组合数

  时间复杂度为$O(N+M)$

  路径的转化是这种算法的关键, 也是巧妙所在

 代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4004, mod = 998244853;

int n, m;
ll fac[maxn], inv[maxn], f[maxn], ans = 0;

void init()
{
    fac[0] = 1LL;
    for(int i = 1; i <= n + m; ++i)
        fac[i] = 1LL * fac[i-1] * i % mod;
    inv[0] = inv[1] = 1LL;
    for(int i = 2; i <= n + m; ++i)
        inv[i] = 1LL * (mod - mod / i) * inv[mod%i] % mod;
    for(int i = 2; i <= n + m; ++i)
        inv[i] = inv[i-1] * inv[i] % mod;
}

ll comb(int x, int y)
{
    return (fac[x] * inv[y] % mod) * inv[x-y] % mod;
}

ll calc(int x)
{
    if(x <= n - m)
        return comb(n + m, n);
    return comb(n + m, n - x);
}

int main()
{
    scanf("%d%d", &n, &m);
    init();
    for(int i = 1; i <= n; ++i)
        f[i] = calc(i);
    for(int i = 1; i <= n; ++i)
        ans = (ans + (1LL * i * (f[i] - f[i+1] + mod) % mod)) % mod;
    printf("%lld\n", ans);
    return 0;
}
View Code

 

おすすめ

転載: www.cnblogs.com/Joker-Yza/p/11613901.html