1. 程式人生 > 實用技巧 >CF295D - Greg and Caves 題解

CF295D - Greg and Caves 題解

Portal

我們考慮將整個洞分成兩半,一半遞增,一半遞減。我們分別 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\)

。那麼我們只需要維護 \(dp_{i,j}\) 的字首和,和 \(-j\cdot dp_{i,j}\) 的字首和即可平方。

然後我們要做的是求答案。考慮列舉最長的那行和那行的長度,平方?你會發現有重複,因為最長的那行後可能有多個。那怎麼辦呢?注意到它們長得像一個火箭筒一樣,我們列舉極大最長行區間 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;
}