【洛谷5204】[USACO19JAN] Train Tracking 2 P(DP)
點(diǎn)此看題面
有一個(gè)值域?yàn)?[1,10^9])的整數(shù)序列,給出每相鄰(k)個(gè)元素中的最小值,求原序列可能的個(gè)數(shù)。
(nle10^5)
(a_i)全相同時(shí)的(DP)
方便起見記(w=10^9-a_i)。
那也就是說,至多隔(k)個(gè)位置選擇一次(a_i),而不選(a_i)的時(shí)候有(w)種選法。
因此設(shè)(f_i)表示第(i)個(gè)位置選擇了的方案數(shù),得出暴力轉(zhuǎn)移式:
[f_i=sum_{j=i-k}^{i-1}f_j imes w^{i-j-1}
]
我們發(fā)現(xiàn)轉(zhuǎn)移的系數(shù)是(w^{i-j-1}),因此考慮把(f_{i-1})乘上(w),發(fā)現(xiàn):
[f_{i-1} imes w=sum_{j=i-k-1}^{i-2}f_j imes w^{i-j-1}
]
它和(f_i)只有(f_{i-k-1})和(f_{i-1})兩項(xiàng)不同,稍微補(bǔ)正一下即可:
[f_i=f_{i-1} imes (w+1)-f_{i-k-1} imes w^k
]
這樣就可以(O(n))去(DP)了。
(a_i)不相同時(shí)的分段拆解
對于(a_i)全相同的一段([i,j]),粗略考慮它在原序列中管轄的區(qū)間為([i,j+k-1]),長度為(j-i+k)。
但是,如果(a_{i-1}>a_i),說明(s_{i+k-1}=a_i),且(s_{isim i+k-2})肯定全都大于等于(a_{i-1}),因此當(dāng)前段管轄區(qū)間大小減少(k)。
同理,如果(a_{j+1}>a_i),說明(s_j=a_i),且(s_{j+1sim j+k-1})肯定全都大于等于(a_{j+1}),因此當(dāng)前段管轄區(qū)間大小減少(k)。
然后發(fā)現(xiàn)不同的管轄區(qū)間之間是獨(dú)立的,分別按照前面的方法(DP)然后把方案數(shù)相乘即可。
代碼:(O(n))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define INF 1000000000
#define X 1000000007
using namespace std;
int n,k,a[N+5];I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int f[N+5];I int DP(CI x,CI n)//動(dòng)態(tài)規(guī)劃
{
RI i,w=INF-x,p=QP(w,k);f[0]=f[1]=1;//初始化
for(i=2;i<=n+1;++i) f[i]=(1LL*f[i-1]*(w+1)-(i>k?1LL*f[i-k-1]*p%X:0)+X)%X;return f[n+1];//利用f[i-1]*w高效轉(zhuǎn)移
}
int main()
{
RI i,j,p;for(scanf("%d%d",&n,&k),i=1;i<=n-k+1;++i) scanf("%d",a+i);
RI t=1,o;for(i=1;i<=n-k+1;i=j+1) {j=i;W(a[j+1]==a[i]) ++j;o=j-i+k-(a[i-1]>a[i])*k-(a[j+1]>a[i])*k,t=1LL*t*DP(a[i],o)%X;}//根據(jù)a[i]分段拆解
return printf("%d
",t),0;
}
敗得義無反顧,弱得一無是處
總結(jié)
以上是生活随笔為你收集整理的【洛谷5204】[USACO19JAN] Train Tracking 2 P(DP)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: t检验中的t值和p值是什么关系_t检验和
- 下一篇: 安装zib扩展报错