【CF GYM101611B】Byteland Trip 題解
阿新 • • 發佈:2018-11-16
題目出自 2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest
題目大意
n 個點排成一排,每個點如果為 “<” 則表示可以向前走,如果為 “>” 則表示可以向後走。
對每個點 i,求以 i 為終點、經過每個點恰好一次的路徑數。
n<=5000
題解
總榜沒人做這個題是什麼鬼啦。。。
對於到達點 i 的每一條路,有時在左邊有時在右邊,不好處理,我們應該想辦法使左右兩邊分別處理然後合併。
dp,設 f[i][j] 表示從左到右做到第 i 個點、形成了 j 個連通塊(每個連通塊是一段路徑,並且最終指向右邊)的方案數,再設 g[i][j] 表示從右到左做到第 i 個點、形成了 j 個連通塊(每個連通塊最終指向左邊)的方案數。
對於點 i,將 f[i-1] 與 g[i+1] 合併即是答案。
求這兩個 dp 陣列很簡單,比如求 f[i],第 i 個點如果是 “<”,則要麼成為某個連通塊的開始,要麼連線兩個連通塊;如果是 “>”,則要麼成為某個連通塊的末尾,要麼新建一個連通塊。
程式碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int maxn=5010;
const LL mo=1e9+7;
int n;
char S[maxn];
LL f[maxn][maxn],g[maxn][maxn],jc[maxn];
int main()
{
jc[0]=1;
fo(i,1,5005) jc[i]=jc[i-1]*i%mo;
scanf("%s",S+1);
n=strlen(S+1);
if (n==1) {printf("1\n"); return 0;}
f[0][0]=1;
fo(i,1,n)
fo(j,1,i)
if (S[i]=='<') f[i][j]=(f[i-1][j]*j+f[i-1][j+1]*(j+1)%mo*j)%mo;
else f[i][j]=(f[i-1][j-1]+f[i-1][j]*j)%mo;
g[n+1][0]=1;
fd(i,n,1)
fo(j,1,n-i+1)
if (S[i]=='<') g[i][j]=(g[i+1][j]*j+g[i+1][j-1])%mo;
else g[i][j]=(g[i+1][j]*j+g[i+1][j+1]*(j+1)%mo*j)%mo;
fo(i,1,n)
{
LL ans=0;
fo(j,0,i-1)
{
if (j) (ans+=jc[j]*jc[j-1]%mo*f[i-1][j]%mo*g[i+1][j-1])%=mo;
(ans+=jc[j]*jc[j]%mo*2%mo*f[i-1][j]%mo*g[i+1][j])%=mo;
(ans+=jc[j]*jc[j+1]%mo*f[i-1][j]%mo*g[i+1][j+1])%=mo;
}
printf("%I64d ",ans);
}
}