[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表示当前区间内的和即可

 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 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/12305315.html