CF295D - Greg and Caves 題解
阿新 • • 發佈:2020-10-20
我們考慮將整個洞分成兩半,一半遞增,一半遞減。我們分別 DP 求值,最後合併。
顯然上下半是對稱的。。我們考慮 \(dp_{i,l,r}\) 表示前 \(i\) 行遞增,第 \(i\) 行區間為 \(l,r\) 的方案數。然後你發現這個空間就炸了。進一步發現,\(r-l+1\) 相等的一些 \([l,r]\) 是等價的,於是我們只記錄區間長度。於是 \(dp_{i,j}\)。
轉移方程(上面空和不空,不空列舉長度):
\[dp_{i,j}=\sum_{k=2}^j(j-k+1)dp_{i-1,k}+1 \]直接算是三方的,考慮將 \(j-k+1\) 拆開得到 \(j+1\) 和 \(-k\)
然後我們要做的是求答案。考慮列舉最長的那行和那行的長度,平方?你會發現有重複,因為最長的那行後可能有多個。那怎麼辦呢?注意到它們長得像一個火箭筒一樣,我們列舉極大最長行區間 instead of 最長行。那這樣就是三方的了啊。為了優化,我們把式子寫出來然後推。
\[\begin{aligned}ans&=\sum_{i=1}^n\sum_{j=i}^n\sum_{k=2}^m(dp_{i,k}-dp_{i-1,k})(dp_{n-j+1,k}-dp_{n-j,k})\\&=\sum_{k=2}^m\sum_{i=1}^n(dp_{i,k}-dp_{i-1,k})\sum_{j=i}^n(dp_{n-j+1,k}-dp_{n-j,k})\end{aligned} \]這樣移一下 \(\sum\),然後可以將最後一個 \(j\) 的 \(\sum\) 給字尾和預處理一下,就是平方了。
感覺這種基礎的 DP 優化還算是我比較行的一項?其他大概就一無是處了吧
#include<bits/stdc++.h> using namespace std; const int mod=1000000007; const int N=2000,M=N; int n,m; int dp[N+1][M+1]; int Sum[N+1][M+1],Sumk[N+1][M+1]; int sum[N+2]; int main(){ cin>>n>>m; for(int i=2;i<=m;i++)dp[1][i]=1,Sum[1][i]=(Sum[1][i-1]+dp[1][i])%mod,Sumk[1][i]=(Sumk[1][i-1]-1ll*i*dp[1][i])%mod; for(int i=2;i<=n;i++)for(int j=2;j<=m;j++){ dp[i][j]=(1ll*(j+1)*Sum[i-1][j]+Sumk[i-1][j]+1)%mod; Sum[i][j]=(Sum[i][j-1]+dp[i][j])%mod; Sumk[i][j]=(Sumk[i][j-1]-1ll*j*dp[i][j])%mod; } int ans=0; for(int k=2;k<=m;k++){ for(int j=n;j;j--)sum[j]=(1ll*sum[j+1]+dp[n-j+1][k]-dp[n-j][k])%mod; for(int i=1;i<=n;i++)(ans+=1ll*(m-k+1)*(dp[i][k]-dp[i-1][k])%mod*sum[i]%mod)%=mod; } cout<<(ans+mod)%mod; return 0; }