普通DP——CF1542D Priority Queue
阿新 • • 發佈:2021-07-12
普通DP——CF1542D Priority Queue
題目傳送門:Priority Queue
這道題目假如有長度為n,那麼子序列就有\(2^n\)個,如果按照n是500的資料範圍那麼肯定是回超時的。既然是算總和,我們只要對於這個序列中的每一個數到底有多少個序列包含了它(我們在這裡叫x)。這樣就可以算出這個數對答案的貢獻。
我們假設\(dp[i][j]\)的意思是在前i 個字串中選取的數中有j個數比x小(注意如果和x相同大,但是位置比x考前那麼也算進去)
然後思考轉移方程式:
- 如果當前這個數字就是x,那麼\(dp[i][j] = dp[i-1][j]\)(一定是取這個數字,不然沒貢獻)
- 如果當前這個數字小於x,或者和x相同大但是位置比x考前\(dp[i][j] = dp[i-1][j]+dp[i-1][j-1]\)
- 如果當前這個數字大於x,或者和x相同大但是位置比x靠後\(dp[i][j] = dp[i-1][j]+dp[i-1][j]\)(前面這個是取,後面是不取)
- 如果當前這個數字是符號,那麼我們再進行分類:
- 如果當前j為0,那麼\(dp[i][0] = dp[i-1][1]+dp[i-1][0]+dp[i-1][j]\)(前兩個是取,後面是不取)
- 如果當前j不是0,那麼\(dp[i][j] = dp[i-1][j+1]+dp[i-1][j]\)(前面是取,後面是不取)
#include<iostream> #include<string> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; const int mod = 998244353; const int maxn = 505; int dp[maxn][maxn]; int num[maxn]; void ini() { memset(dp, 0, sizeof(dp)); dp[0][0] = 1; } int main() { int n; char ope; ll ans = 0; scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf(" %c", &ope); if(ope == '-') num[i] = -1; else scanf("%d", &num[i]); } for(int i = 1; i <= n; i++) { if(num[i] == -1) continue; ini(); for(int z = 1; z <= n; z++) { if(i == z) { for(int g = 0; g <= z; g++) dp[z][g] = dp[z-1][g]; } else if(num[z] == -1) { if(z < i) dp[z][0] = (0ll + dp[z-1][0]*2 + dp[z-1][1])%mod; else dp[z][0] = (0ll + dp[z-1][0]+dp[z-1][1])%mod; for(int g = 1; g <= z; g++) { dp[z][g] = (0ll + dp[z-1][g+1] + dp[z-1][g])%mod; } } else if(num[z] < num[i] || (num[z] == num[i] && z < i)) { dp[z][0] = dp[z-1][0]; for(int g = 1; g <= z; g++) dp[z][g] = (0ll + dp[z-1][g] + dp[z-1][g-1])%mod; } else { for(int g = 0; g <= z;g++) dp[z][g] = (2ll*dp[z-1][g])%mod; } } for(int g = 1; g <= n; g++) dp[n][g] = (dp[n][g] + dp[n][g-1])%mod; ans = (1ll*dp[n][n]*num[i] + ans)%mod; } printf("%lld", ans); return 0; }