1. 程式人生 > 其它 >題解 P7634 [COCI2010-2011#5] HONI

題解 P7634 [COCI2010-2011#5] HONI

題目傳送門

演算法分析:線性 dp

應該來說是比較明顯的線性 dp,關鍵在於如何設計狀態及轉移方程。

在本題中涉及兩種“題目”,即僅能被視為一個難度的“題目”和能被視為有兩個連續難度的“題目”。作為兩種不同的情況,一維的 dp 明顯是不夠的,需要加入第二維。

為了表述方便,下文稱“僅能被視為一個難度的‘題目’”為“第一種題目”,其數量記為 \(a_i\);稱“能被視為有兩個連續難度的‘題目’”為“第二種題目”,其數量記為 \(b_i\)。在分析中,將省略“取模”這一過程。

接下來分析 dp 的狀態及轉移方程。

狀態:

我們設計 \(dp_{i,j}\) 表示對於難度 \(i\) ,是用方法 \(j\)

解決的。其中 \(j\in\{0,1\}\),在此記 \(j=0\) 為使用了第一種題目,\(j=1\) 為使用了第二種題目(當然,反過來也一樣)。

邊界:

根據狀態,顯然地,\(dp_{1,0}=a_1,dp_{1,1}=b_1\),要求的答案為 \(dp_{n,0}+dp_{n,1}\)

轉移方程:

  • 根據題意,對於 \(j=0\) 的情況,要考慮難度 \(i-1\) 的使用情況。若上一個使用了第一種題目,那麼其貢獻為 \(dp_{i-1,0}\times(a_i+b_{i-1})\),若上一個使用了相應的第二種題目,則貢獻為 \(dp_{i-1,1}\times(a_i+\max(b_{i-1}-1,0))\)

    。由於上一個使用了第二種題目,數量須 \(-1\)。同時為了防止 \(b_{i-1}=0\) 的情況,與 \(0\) 取最大值,增強程式的容錯率。

  • 對於\(j=1\) 的情況較為簡單,兩種情況的貢獻各為 \(dp_{i-1,0}\times b_i\) 以及 \(dp_{i-1,1}\times b_i\)

    綜上所述,有:

    \[dp_{i,j}=\begin{cases}dp_{i-1,0}\times(a_i+b_{i-1})+dp_{i-1,1}\times(a_i+\max(b_{i-1}-1,0))&j=0\\dp_{i-1,0}\times b_i+dp_{i-1,1}\times b_i&j=1\end{cases} \]

在此過程中,\(i:2\to n\),時間複雜度為 \(\mathcal{O}(n)\),滿足條件。

下面給出程式碼:

#include<bits/stdc++.h>
#define ll long long
#define reg register
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline int read();
const int N=1e5+10,mod=1e9+7;
int n,a[N],b[N];
ll dp[N][2];
int main() {
	n=read();
	F(i,1,n)a[i]=read();
	F(i,1,n-1)b[i]=read();
	dp[1][0]=a[1],dp[1][1]=b[1];
	//初始化 
	F(i,2,n){
		dp[i][0]=(dp[i-1][0]*(a[i]+b[i-1])+dp[i-1][1]*(a[i]+max(b[i-1]-1,0)))%mod;
		dp[i][1]=(dp[i-1][0]*b[i]+dp[i-1][1]*b[i])%mod;
	}//轉移 
	printf("%lld",(dp[n][0]+dp[n][1])%mod);
	return 0;
}
inline int read() {
	reg int x=0;
	reg char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

寫到這裡已經足夠了,但事實上,空間仍然可以優化。觀察到 \(dp_{i,0}\)\(dp_{i,1}\) 均由 \(dp_{i-1,0}\)\(dp_{i-1,1}\) 迭代轉移過來,因此可以用一個變數來滾動記錄。下面給出利用變數滾動記錄的程式碼:

	x=a[1],y=b[1];
	F(i,2,n){
		ll xx=(x*(a[i]+b[i-1])+y*(a[i]+max(0,b[i-1]-1)))%mod;
		ll yy=(x*b[i]+y*b[i])%mod;
        //滾動
		x=xx,y=yy;
	}

兩個程式碼在本質上是完全一致的。

AC

歡迎交流討論,請點個贊哦~