USACO 2019 January Contest Platinum T3: Train Tracking 2

题目大意

每天特快列车都会经过农场。列车有N节车厢(1≤N≤10^5),每节车厢上有一个1到10^9之间的正整数编号;不同的车厢可能会有相同的编号。 平时,Bessie会观察驶过的列车,记录车厢的编号。但是今天雾实在太浓了,Bessie一个编号也看不见!幸运的是,她从城市里某个可靠的信息源获知了列车编号序列的所有滑动窗口中的最小值。具体地说,她得到了一个正整数K,以及N−K+1个正整数c1,…,cN+1−K,其中ci是车厢i,i+1,…,i+K−1之中编号的最小值。

帮助Bessie求出满足所有滑动窗口最小值的对每节车厢进行编号的方法数量。由于这个数字可能非常大,只要你求出这个数字对10^9+7取余的结果Bessie就满意了。

Bessie的消息是完全可靠的;也就是说,保证存在至少一种符合要求的编号方式。

题目分析

一上来似乎题目条件无从入手,所以我们首先考虑此题的一个弱化版本:如果所有 ci 都相等时该怎么做。

现在假设有 len个数,取值从 到 1e9,而且每连续 个数至少有一个是 v 。

那么取值就只有 v 和 >v 两种取值了,>v 的取值有1e9v 种,令 x=1e9 - v 。

那么有一个显然的dp,fi 表示这个前i个数的答案

fi (x+1fi-1 − xk fik1

第 i 个数随便选,乘上前 i1 个数的答案,这时可能出现问题,就是第 ik+1个数到第i个数都 >v 导致了不合法,所以要减掉这些情况

为什么减掉的是xk fik1呢,显然这 个数的放法共 x种没有问题,要注意一下从第 ik+1个数到第 i1个数都 >v,

那么只有第 ik 个数取值是 v 才能够满足最小值的条件,所以前面的取值方案数是  fik1

考虑完这个后,我们就可以入手了。

令 f(v,len)表示 "有 len个数,取值从 v 到 1e9,而且每连续 k 个数至少有一个是 v" 这个问题的答案

首先,显然可以将一段相等的 ci 合并起来

对于一个段 ci==cj,如果全部只有这一段,方案数为ss(ci,ji+k)

如果有一个 ci1​ ci

可得

min{ai1,,ai+k2}=ci1

相当于 min{ai1,,ai+k2} ≥ ci1​ 

又因为 min{ai,,ai+k1}=ci

前面已经推出 ai,,ai+k2​ ≥ ci1​ ci了,所以这些都不可能是最小值

所以对于 ci 这一段的最小值只能有一个就是  ai+k1=ci

前面的数也都在前一段的范围内计算过了

所以这一段相当于最前面 k 个数就没了,其中前 k1个数会在前面段计算答案,第k个数只有一个取值 ci

对于后面也同理,如果 cj+1>c则后面 k 个数也没了

所以一段的 len 实际上是 ji+k 减去 0~2 个 k

对于所有的段依次求解最后方案数乘起来即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const ll Mod=1e9+7;
 5 const int MAXN=1e5+10;
 6 int n,k;
 7 ll c[MAXN];
 8 ll f[MAXN];
 9 
10 inline ll ksm(ll x,ll y){
11     ll res=1;
12     while(y){
13         if(y&1) res=res*x%Mod;
14         x=x*x%Mod;
15         y>>=1;
16     }
17     return res;
18 } 
19 
20 inline int Solve(ll v,ll len){
21     ll x=1e9-v,xp=ksm(x,k);
22     f[0]=f[1]=1;
23     for(int i=2;i<=len+1;++i){
24         f[i]=(x+1)*f[i-1]%Mod;
25         if(i-k-1>=0)
26             f[i]=(f[i]-xp*f[i-k-1]%Mod+Mod)%Mod;
27     }
28     return f[len+1]; 
29 }
30 int main(){
31     scanf("%d%d",&n,&k);
32     for(int i=1;i<=n-k+1;++i)
33         scanf("%lld",&c[i]);
34     ll ans=1,len;
35     for(int i=1,j;i<=n-k+1;i=j+1){
36         j=i;
37         while(c[j+1]==c[i]) ++j;
38         len=(ll)j-i+k;
39         if(i!=1&&c[i-1]>c[i]) len-=k;
40         if(j!=n-k+1&&c[j+1]>c[i]) len-=k;
41         if(len) ans=ans*Solve(c[i],len)%Mod;
42     }
43     printf("%lld",ans);
44     return 0;
45 }

 

猜你喜欢

转载自www.cnblogs.com/LI-dox/p/11223731.html
今日推荐