2019牛客第五場(chǎng)G題 (subsequence 1) DP

題意:給一個(gè)數(shù)字字符串s和t,求s中有多少個(gè)子序列比t更大

題解:如果子序列比t更長(zhǎng),那么只要開頭不是0都可以,暴力枚舉做這件事就好了。問題在于子序列和t等長(zhǎng)的情形。從前往后考慮很麻煩,試著從后往前考慮:dp[i][j] 表示的是s中以i開始的后綴的子序列匹配了t的j以后的子串。

于是有:
$$
dp[i][j]=
\begin{cases}
dp[i + 1][j] + dp[i + 1][j + 1] & s[i]=t[j]\
dp[i + 1][j] + C(n - i, m - j)& s[i]>t[j]\
dp[i + 1][j]& s[i]<t[j]

\end{cases}
$$
每次轉(zhuǎn)移都是O(1)的,然后這個(gè)題就做完了。

#include <algorithm>
#include <iostream>
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
const int maxn = 3005;
char s[maxn], t[maxn];
using namespace std;
using ll = long long;
const ll mod = 998244353;
ll dp[maxn][maxn];

ll bin(ll x, ll n, ll MOD)
{
   ll ret = MOD != 1;
   for (x %= MOD; n; n >>= 1, x = x * x % MOD)
       if (n & 1)
           ret = ret * x % MOD;
   return ret;
}
inline ll get_inv(ll x, ll p) { return bin(x, p - 2, p); }

ll invf[maxn], fac[maxn] = {1};
void fac_inv_init(ll n, ll p)
{
   FOR(i, 1, n)
   fac[i] = i * fac[i - 1] % p;
   invf[n - 1] = bin(fac[n - 1], p - 2, p);
   FORD(i, n - 2, -1)
   invf[i] = invf[i + 1] * (i + 1) % p;
}

inline ll C(ll n, ll m)
{ // n >= m >= 0
   return n < m || m < 0 ? 0 : fac[n] * invf[m] % mod * invf[n - m] % mod;
}

int main()
{
   fac_inv_init(maxn, mod);
   ios::sync_with_stdio(false);
   int round;
   cin >> round;
   while (round--)
   {
       int n, m;
       cin >> n >> m;
       cin >> (s + 1) >> (t + 1);
       ll ans = 0;
       for (int i = 1; i <= n; i++)
       {
           if (s[i] != '0')
           {
               for (int j = m; j <= n - i; j++)
               {
                   ans = (ans + C(n - i, j)) % mod;
               }
           }
       }
       for (int i = 0; i <= n + 1; i++)
       {
           for (int j = 0; j <= m + 1; j++)
           {
               dp[i][j] = 0;
           }
       }

       for (int i = n; i >= 1; i--)
       {
           for (int j = m; j >= 1; j--)
           {
               if (s[i] == t[j])
               {
                   dp[i][j] = 
                     (dp[i + 1][j] + dp[i + 1][j + 1]) % mod;
               }
               else if (s[i] > t[j])
               {
                   dp[i][j] = 
                     (dp[i + 1][j] + C(n - i, m - j)) % mod;
               }
               else
               {
                   dp[i][j] = dp[i + 1][j];
               }
           }
       }
       cout << (dp[1][1] + ans) % mod << endl;
   }
}


預(yù)處理組合數(shù)的部分來自 ECNU 退役隊(duì)伍 F0RE1GNERS 的模板 https://github.com/zerolfx/template

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容