1. 程式人生 > >2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest B - Byteland Trip dp

2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest B - Byteland Trip dp

B - Byteland Trip

題目大意:給你一個由'<' 和 '>'組成的串, 如果在'<' 只能前往編號比它小的任意點, 反之只能前往比它大的任意點,問你能遍歷所有點

並且每個點只走一次終點在各個位置的方案數。

思路:感覺這種右能從左邊跑到右邊又跑回來的dp很難搞,如果我們確定一個終點, 如果知道它左邊點一共出來幾次,右邊的點一共出來幾次

那麼方案數就很好求了, 所以我們定義dp1[ i ][ j ] 表示前 i 個點,遍歷所有點並且向右出去 j 次的的方案數, dp2[ i ][ j ]就是反過來的。

對於每個點,我們列舉左邊出來幾次就好啦。

#include<bits/stdc++.h>
#define
LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define PLI pair<LL, int> #define ull unsigned long long using namespace std; const int N = 5007; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 + 7
; const double eps = 1e-8; int n, m; LL dp1[N][N], dp2[N][N], f[N], ans[N]; char s[N]; void add(LL &a, LL b) { a += b; if(a >= mod) a -= mod; } int main() { for(int i=f[0]=1; i < N; i++) f[i] = f[i-1]*i%mod; scanf("%s", s + 1); m = strlen(s + 1); for(int
i = 1; i <= m; i++) if(s[i] == '<' || s[i] == '>') s[++n] = s[i]; if(n == 1) { puts("1"); return 0; } dp1[0][0] = 1; for(int i = 1; i <= n; i++) { for(int j = 0; j <= i; j++) { if(s[i] == '<') { add(dp1[i][j], dp1[i-1][j]*j%mod); add(dp1[i][j], dp1[i-1][j+1]*(j+1)%mod*j%mod); } else { if(j) add(dp1[i][j], dp1[i-1][j-1]); add(dp1[i][j], dp1[i-1][j]*j%mod); } } } dp2[n+1][0] = 1; for(int i = n; i >= 1; i--) { for(int j = 0; j <= n-i+1; j++) { if(s[i] == '<') { if(j) add(dp2[i][j], dp2[i+1][j-1]); add(dp2[i][j], dp2[i+1][j]*j%mod); } else { add(dp2[i][j], dp2[i+1][j]*j%mod); add(dp2[i][j], dp2[i+1][j+1]*(j+1)%mod*j%mod); } } } ans[1] = dp2[2][1]; ans[n] = dp1[n-1][1]; for(int i = 2; i < n; i++) { for(int j = 1; j < i; j++) { add(ans[i], dp1[i-1][j]*dp2[i+1][j]%mod*f[j]%mod*f[j]%mod*2%mod); add(ans[i], dp1[i-1][j]*dp2[i+1][j+1]%mod*f[j]%mod*f[j+1]%mod); if(j > 1) add(ans[i], dp1[i-1][j]*dp2[i+1][j-1]%mod*f[j]%mod*f[j-1]%mod); } } for(int i = 1; i <= n; i++) printf("%lld ", ans[i]); return 0; } /* */