[luogu5204]Train Tracking 2
考虑一个位置的上界,即$bi=min(c_{i-k+1},c_{i-k+2},……,ci)$,那么每一个位置有两种方式:1.达到上界;2.未达到上界
那么可以将权值相同的ci和bi提出来,由于权值不同的ci是独立的,因此直接将每一个的方案数乘起来即可
提出来以后,问题转化为每一个bi可以覆盖提出来的ci的一段区间,然后覆盖整个区间的方案数,由于这个区间的左右端点都不下降,因此可以用f[i][j]表示前i个bi恰好覆盖了前j个ci的方案数,转移为$f[i][j]=f[i-1][j]*(C-1)+(j==ri)*\sum_{k=li-1}^{ri}f[i-1][j]$
容易发现对于大部分的f[j]都只乘上了一个$C-1$,而仅有ri要特殊处理,这个东西可以用线段树来维护,但同时发现li和ri不断递增,所以只需要维护一个s表示当前区间内的和即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define mod 1000000007 5 multiset<int>s; 6 map<int,vector<int> >m1,m2; 7 map<int,vector<int> >::iterator it; 8 int n,m,b[N],c[N],f[N]; 9 int ksm(int n,int m){ 10 if (!m)return 1; 11 int s=ksm(n,m>>1); 12 s=1LL*s*s%mod; 13 if (m&1)s=1LL*s*n%mod; 14 return s; 15 } 16 int main(){ 17 scanf("%d%d",&n,&m); 18 for(int i=1;i<=n-m+1;i++){ 19 scanf("%d",&c[i]); 20 c[i]=mod-6-c[i]; 21 } 22 for(int i=1;i<=n;i++){ 23 if (i<=n-m+1)s.insert(c[i]); 24 if (m<i)s.erase(s.find(c[i-m])); 25 b[i]=(*s.begin()); 26 } 27 for(int i=1;i<=n-m+1;i++)m1[c[i]].push_back(i); 28 for(int i=1;i<=n;i++)m2[b[i]].push_back(i); 29 int ans=1; 30 for(it=m1.begin();it!=m1.end();it++){ 31 int x=(*it).first,tag=1,s=1; 32 f[0]=1; 33 for(int i=1;i<=m1[x].size();i++)f[i]=0; 34 for(int i=0,j=0,k=0;i<m2[x].size();i++){ 35 while ((j<m1[x].size())&&(m1[x][j]<=m2[x][i]-m))s=(s+mod-1LL*f[j++]*tag%mod)%mod; 36 while ((k<m1[x].size())&&(m1[x][k]<=m2[x][i]))s=(s+1LL*f[++k]*tag)%mod; 37 tag=tag*(x-1LL)%mod; 38 f[k]=(f[k]+1LL*s*ksm(tag,mod-2))%mod; 39 s=1LL*s*x%mod; 40 } 41 ans=1LL*tag*f[m1[x].size()]%mod*ans%mod; 42 } 43 printf("%d",ans); 44 }