题解 P5204 【[USACO19JAN]Train Tracking 2】

· · 题解

毒毒题,对着嘤文题解看了贼久

首先考虑此题的一个弱化版本:如果输入的所有c_i相等怎么做

现在假设有len个数,取值从v10^9,而且每连续k个数至少有一个是v

那么取值就只有v>v两种取值了,>v的取值有10^9-v种,设为x

那么有一个显然的dp,f_i表示这个问题i个数的答案

枚举这个数列的最后一个取值为v的数,假设是第j个,那么后面的i-j个数有x种选法

f_i=\sum_{j=i-k+1}^{i}x^{i-j}f_{j-1}

这个dp显然是不行的,还有一个dp,也是设f_i表示这个问题i个数的答案

f_i=(x+1)f_{i-1}-x^kf_{i-k-1}

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

为什么减掉的是x^kf_{i-k-1}呢,显然这k个数的放法共x^k种没有问题,要注意一下从第i-k+1个数到第i-1个数都>v,那么只有第i-k个数取值是v才能够满足最小值的条件,所以前面的取值方案数是f_{i-k-1}

il int solve(int v,int len){
    int x=1000000000-v,xk=pow(x,k);
    f[0]=f[1]=1;
    for(int i=2;i<=len+1;++i){
        f[i]=1ll*(x+1)*f[i-1]%mod;
        if(i-k-1>=0)f[i]=(f[i]-1ll*xk*f[i-k-1]%mod+mod)%mod;
    }
    return f[len+1];
}

那么解决了前面的问题,后面的也很好办

s(v,len)表示现在假设有len个数,取值从v10^9,而且每连续k个数至少有一个是v问题的答案,可以在O(len)时间内球解

首先,可以将一段相等的c合并起来

然后(开始口胡)

对于一个段c_i=\cdots=c_j,如果只有这一段,方案数为s(c_i,j-i+k)

如果有一个c_{i-1}>c_i

那么可以知道的是

\min\{a_{i-1},\cdots,a_{i+k-2}\}=c_{i-1}

这里可以推出a_{i-1},\cdots,a_{i+k-2}\geq c_{i-1}

\min\{a_{i},\cdots,a_{i+k-1}\}=c_{i}

前面已经推出a_{i},\cdots,a_{i+k-2}\geq c_{i-1}>c_{i}了,所以这些都不可能是最小值

这一段的最小值只能有一个就是a_{i+k-1}=c_{i}

前面的数也都在前一段的范围内

所以这一段最前面k个数就没了,其中前k-1个数会在前面段计算答案,第k个数只有一个取值c_i

对于后面也同理,如果c_{j+1}>c_j则后面k个数没了

所以一段的len实际上是j-i+k减去0到2个k

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

代码极为简单

#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 1000000007
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,k;
int a[100010];
int f[100010];
il int pow(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;y>>=1;
    }
    return ret;
}
il int solve(int v,int len){
    int x=1000000000-v,xk=pow(x,k);
    f[0]=f[1]=1;
    for(int i=2;i<=len+1;++i){
        f[i]=1ll*(x+1)*f[i-1]%mod;
        if(i-k-1>=0)f[i]=(f[i]-1ll*xk*f[i-k-1]%mod+mod)%mod;
    }
    return f[len+1];
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    n=gi(),k=gi();
    for(int i=1;i<=n-k+1;++i)a[i]=gi();
    int ans=1,len;
    for(int i=1,j;i<=n-k+1;i=j+1){
        j=i;
        while(a[j+1]==a[i])++j;
        len=j-i+k;
        if(i!=1&&a[i-1]>a[i])len-=k;
        if(j!=n-k+1&&a[j+1]>a[i])len-=k;
        if(len>0)ans=1ll*ans*solve(a[i],len)%mod;
    }
    printf("%d\n",ans);
    return 0;
}