1. 程式人生 > 其它 >[模擬賽20211004] Lost Operators

[模擬賽20211004] Lost Operators

題目

你有一個缺少運算子的表示式,其中只會包含 \(1\sim 9\) 的單位數字,減號(代替缺失的運算子),以及括號。保證表示式是合法的。

現在需要向減號的位置填入加號或者乘號。求出所有不同的填寫方案中,包含 \(k\) 個加號的表示式的值的和,對於 \(10^9+7\) 取模。

資料範圍:對於 \(100\%\) 的資料,表示式長度不超過 \(10^5\)減號數量不超過 \(2500\)

分析

不妨考慮一個簡單的情況,如果沒有括號該如何解決?

顯然我們可以設計 DP:設考慮完前 \(i\) 個減號後,包含 \(j\) 個加號的表示式的和為 \(f_{i,j}\),方案數為 \(g_{i,j}\)

假如有 \(m\) 個減號。數碼從 0 開始編號,分別為 \(a_0,a_1,\dots,a_m\),而減號從 1 開始編號。則轉移方程可以方便地寫作:

\[\begin{aligned} g_{i,j}&=\sum_{k=1}^{i}g_{k-1,j-1}\\ f_{i,j}&=\sum_{k=1}^{i}\left(g_{k-1,j-1}\prod_{p=k}^ia_p+f_{k-1,j-1}\right) \end{aligned} \]

可以看出,我們可以按照 \(j\) 來劃分轉移,而每一層轉移之間可以用字首和優化。這是一種轉移方式。

另一種轉移方式是,我們可以在過程中直接維護好 \(\sum_{k=1}^{i}g_{k-1,j-1}\prod_{j=k}^ia_j\)

這個關鍵的值,這樣就可以一路轉移到底了。


現在存在括號,我們將每一個括號內部的資訊看作一個整體,這樣在一個括號之內,問題的結構與沒有括號的問題類似。只不過,現在每個“數碼”可以貢獻多個加號

考慮到加號個數始終是求和的關係,我們可以想到利用卷積——也即利用多項式表示 DP 的資訊來簡化形式。

因此,在 \(u\) 這個括號塊內,假如有 \(m\)當層的減號。我們仍然可以將“數碼”從 0 開始編號,第 \(i\) 塊的方案數為 \(v_i(x)\),而表示式求和為 \(w_i(x)\)

現在狀態 \(F_i(x)\) 表示考慮了前 \(i\) 個減號後,表示式的和,\(G_i(x)\) 表示方案數。

類似地,可以得到轉移如下,需要注意 \(v,w\) 帶來的變化:

\[\begin{aligned} G_i&=\sum_{k=1}^{i}G_{k-1}\prod_{j=k}^iv_j\\ F_i&=\sum_{k=1}^i\left(G_{k-1}\prod_{j=k}^iw_j+F_{k-1}\prod_{j=k}^iv_j\right) \end{aligned} \]

類似地記錄 \(\sum G\prod w\)\(\sum F\prod v\) 的字尾和即可完成轉移。

對於單塊括號我們如是處理,對於原表示式,我們可以按照括號塊構建樹,在樹上 DP。

複雜度也即樹上揹包的複雜度,可以 \(O(s^2)\) 的時間內完成,其中 \(s\) 為總的減號數量。

小結:

  1. 需要考慮到多種轉移方式。積累一下中途記錄字尾和的轉移,實際上是將原先的 DP 拆分成了子 DP 來維護
  2. 熟練運用多項式來簡化形式;
  3. 這裡只有括號而沒有明確的算符,因此我們選擇用括號資訊來劃分層!。

程式碼

#include <algorithm>
 
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
 
const int mod = 1e9 + 7;
const int MAXN = 1e5 + 5, MAXM = 2505;
 
template<typename _T>
void read( _T &x )
{
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = - x;
}
 
template<typename _T>
void write( _T x )
{
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}
 
std :: vector<int> pos[MAXN];
 
int f[MAXM][MAXM], g[MAXM][MAXM], p[MAXM][MAXM], q[MAXM][MAXM], tmp[MAXM];
 
int mat[MAXN], stk[MAXN], siz[MAXN], top;
char str[MAXN];
 
int N, K, ID = 0;
 
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
 
inline void Upt( int &x, const int v ) { x = Add( x, v ); }
 
int Build( const int l, const int r, const int dep )
{
    if( l == r ) return '0' - str[l];
    if( mat[l] == r ) return Build( l + 1, r - 1, dep + 1 );
    int u = ++ ID;
    int beg = std :: lower_bound( pos[dep].begin(), pos[dep].end(), l ) - pos[dep].begin(),
        ed = std :: upper_bound( pos[dep].begin(), pos[dep].end(), r ) - pos[dep].begin() - 1;
    int v = Build( l, pos[dep][beg] - 1, dep );
    if( v < 0 ) 
    {
        int val = - v; v = MAXM - 1;
        siz[v] = 0, f[v][0] = val, g[v][0] = 1;
    }
    siz[u] = siz[v];
    rep( i, 0, siz[u] )
        p[u][i] = f[u][i] = f[v][i], g[u][i] = g[v][i];
    for( int i = beg ; i <= ed ; i ++ )
    {
        int v = Build( pos[dep][i] + 1, ( i == ed ? r + 1 : pos[dep][i + 1] ) - 1, dep );
        if( v < 0 )
        {
            int val = - v; v = MAXM - 1;
            siz[v] = 0, f[v][0] = val, g[v][0] = 1;
        }
        rep( j, 0, siz[u] + siz[v] + 1 ) tmp[j] = 0;
        per( j, siz[u], 0 ) per( k, siz[v], 0 )
        {
            Upt( tmp[j + k + 1], Mul( g[u][j], f[v][k] ) );
            Upt( tmp[j + k], Mul( p[u][j], f[v][k] ) );
        }
        rep( j, 0, siz[u] + siz[v] + 1 ) p[u][j] = tmp[j], tmp[j] = 0;
        per( j, siz[u], 0 ) per( k, siz[v], 0 )
        {
            Upt( tmp[j + k + 1], Mul( f[u][j], g[v][k] ) );
            Upt( tmp[j + k], Mul( q[u][j], g[v][k] ) );
        }
        rep( j, 0, siz[u] + siz[v] + 1 ) q[u][j] = tmp[j], tmp[j] = 0;
        rep( j, 0, siz[u] + siz[v] + 1 ) f[u][j] = Add( p[u][j], q[u][j] );
        per( j, siz[u], 0 ) per( k, siz[v], 0 )
        {
            Upt( tmp[j + k + 1], Mul( g[u][j], g[v][k] ) );
            Upt( tmp[j + k], Mul( g[u][j], g[v][k] ) );
        }
        rep( j, 0, siz[u] + siz[v] + 1 ) g[u][j] = tmp[j];
        siz[u] += siz[v] + 1;
    }
    return u;
}
 
int main()
{
    read( N ), read( K );
    scanf( "%s", str + 1 );
    rep( i, 1, N ) 
    {
        if( str[i] == '(' ) stk[++ top] = i;
        if( str[i] == ')' ) mat[i] = stk[top], mat[stk[top --]] = i;
        if( str[i] == '-' ) pos[top].push_back( i );
    }
    int rt = Build( 1, N, 0 );
    if( rt <= 0 ) write( - rt ), putchar( '\n' );
    else write( f[rt][K] ), putchar( '\n' );
    return 0;
}