[Sdoi2010] 地精部落
F. 地精部落
題目描述
傳說很久以前,大地上居住著一種神秘的生物:地精。 地精喜歡住在連綿不絕的山脈中。具體地說,一座長度為 N 的山脈 H可分 為從左到右的 N 段,每段有一個獨一無二的高度 Hi,其中Hi是1到N 之間的正 整數。 如果一段山脈比所有與它相鄰的山脈都高,則這段山脈是一個山峰。位于邊 緣的山脈只有一段相鄰的山脈,其他都有兩段(即左邊和右邊)。 類似地,如果一段山脈比所有它相鄰的山脈都低,則這段山脈是一個山谷。 地精們有一個共同的愛好——飲酒,酒館可以設立在山谷之中。地精的酒館 不論白天黑夜總是人聲鼎沸,地精美酒的香味可以飄到方圓數里的地方。 地精還是一種非常警覺的生物,他們在每座山峰上都可以設立瞭望臺,并輪 流擔當瞭望工作,以確保在第一時間得知外敵的入侵。 地精們希望這N 段山脈每段都可以修建瞭望臺或酒館的其中之一,只有滿足 這個條件的整座山脈才可能有地精居住。 現在你希望知道,長度為N 的可能有地精居住的山脈有多少種。兩座山脈A 和B不同當且僅當存在一個 i,使得 Ai≠Bi。由于這個數目可能很大,你只對它 除以P的余數感興趣。
輸入格式
僅含一行,兩個正整數 N, P。
輸出格式
僅含一行,一個非負整數,表示你所求的答案對P取余 之后的結果。
樣例
樣例輸入
4 7樣例輸出
3數據范圍與提示
對于 20%的數據,滿足 N≤10;
對于 40%的數據,滿足 N≤18;
對于 70%的數據,滿足 N≤550;
對于 100%的數據,滿足 3≤N≤4200,P≤10^9?9??
大佬們都說這道題是水題,然而我貌似搞了一天,其實開始就想出來了一個n3的算法,但是肯定會T,我以為是個組合數的題,就去想其他解法了,然而那個n3再改一下就是正解了……
題解:
設f[i][j][0]表示考慮前i段,前i個數,第i段為j且為山谷,f[i][j][1]第i段為j且為山峰。那么f[i][j][0]=∑f[i-1][k][1];(j<k<i)。
這樣是n3怎么優化呢?可以發現f[i][j][0]與f[i][i-j+1][1]是一一對應的(相當于是把原來的山峰變成了山谷,山谷變成了山峰),不明白可以去手膜樣例。所以f[i][j][0]=∑f[i-1][i-k+1][0](j+1<=k<i)=∑f[i-1][k][0](1<=k<=i-j);
其實后面的0就完全可以去掉了:f[i][j]=∑f[i-1][k](1<=k<=i-j),那么可以用一個變量輔助dp,就可以變為n2了。
1 for(int i=2;i<=n;i++) 2 { 3 tem=0; 4 for(int k=1;k<=i-1;k++) 5 tem=(tem+f[i-1][k])%p; 6 for(int j=1;j<=i;j++) 7 f[i][j]=tem,tem=((tem-f[i-1][i-j])%p+p)%p; 8 }?
最后答案為(∑f[n][i])*2,因為最后一個點為山峰和山谷的方案數是一樣的,乘2即可。
#include<iostream> #include<cstdio> #define LL long long using namespace std; LL n,p; LL f[4210][4210]; signed main() {scanf("%lld%lld",&n,&p);LL tem=0,ans=0;f[1][1]=1;for(int i=2;i<=n;i++){tem=0;for(int k=1;k<=i-1;k++) tem=(tem+f[i-1][k])%p;for(int j=1;j<=i;j++)f[i][j]=tem,tem=((tem-f[i-1][i-j])%p+p)%p;}for(int i=1;i<=n;i++)ans=(ans+f[n][i])%p;ans=(ans+ans)%p;cout<<ans<<endl; }?但是這樣在bzoj上A不了,要用滾動數組;
#include<iostream> #include<cstdio> #define LL long long using namespace std; LL n,p,tem=0,ans=0;; LL f[2][4210]; signed main() {scanf("%lld%lld",&n,&p);f[1][1]=1;for(int i=2;i<=n;i++){tem=0;for(int k=1;k<=i-1;k++) tem=(tem+f[(i-1)%2][k])%p;for(int j=1;j<=i;j++)f[i%2][j]=tem,tem=((tem-f[(i-1)%2][i-j])%p+p)%p;for(int k=0;k<=n;k++)f[(i-1)%2][k]=0;}for(int i=1;i<=n;i++)ans=(ans+f[n%2][i])%p;ans=(ans+ans)%p;cout<<ans<<endl; }?
轉載于:https://www.cnblogs.com/Al-Ca/p/11120516.html
總結
以上是生活随笔為你收集整理的[Sdoi2010] 地精部落的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac入门--Apache/Php/My
- 下一篇: PPT转PDF方法