2020GDUTACMA10月月赛 H.一道难题(计数DP)

H.一道难题

Time Limit: 1 Sec
Memory Limit: 128 MB

Description

有一个长度为n的序列,特征为m,满足a[i]=a[i-m] (i>m),a[i]=i (i<=m)。

1,2,…,m[i]-1 ,m[i],1,2,…,m[i]-1,m[i],…(n-i) mod (m[i]+1)

例如 n=7,m=3时,序列如下:

1 ,2, 3, 1, 2, 3, 1

现可删除若干元素(也可以不删,也可以全删),问会出现多少不同的序列? 序列不同当且仅当长度不一致或者某一位不同

答案对1e9+7取模 即ans%1000000007

Input

第一行输入一个T,表示T组数据。(1<=T<=1000)

输入两个整数n,m(1<=m,n<=1e6)

保证输入的n的总和不超过1000000。

Output

输出会出现多少不同的序列

Sample Input

2
6 1
4 3

Sample Output

7
15

思考过程

一开始考虑统计,把类似于 123123123 分割为一个个 123 ,然后分别求出单个的贡献(如 123 能构成的所有非空子序列,共 1,2,12,3,13,23,123 7 个)然后将其分割总数的贡献相乘。

但并不好处理。

考虑 123123 的子序列 123***12***31***23 ,此时三者表示同一个序列 123,该序列已被第一个分隔区间的子序列 123 贡献,但可以看到还需要对 123123 产生的重复贡献进行去重,跨区间的去重处理较为麻烦。

思考如何避免该类复杂计算。

注意到这种重复计数产生于连续空缺长度大于等于 m m m 的情况。
以此作为突破口,存在递推计数手段。

对于 123123 中的第一个 123 中的 1 来说,后面两个 2123***12***31***23 对于 1 来说两个 2 的地位相等。

那么按元素最靠左的那个子序列来对答案做出贡献,或者说使得某个子序列尽早地为答案做出贡献。

因为第一个 2 已经对第一个 1 产生过贡献。可以认为第二个 2 已经无需对第一个 1 做出贡献;那么得出某一位贡献的转移范围不会超过 m m m

则能够设出转移状态 d p i dp_i dpi 表示该序列以第 i i i 个数字作结尾时的子序列与前面所有子序列不相同的有多少个。
d p 0 = 1 dp_0=1 dp0=1 表示空串,转移式子为 d p k = ∑ i = m a x ( k − m , 0 ) k − 1 d p i      ( 0 < k ≤ n ) dp_k =\sum_{i=max(k-m,0)}^{k-1} dp_i \ \ \ \ (0<k \le n) dpk=i=max(km,0)k1dpi    (0<kn)

那么可以得出答案 a n s = ∑ i = 0 n d p i ans=\sum_{i=0}^n dp_i ans=i=0ndpi

记得取模即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int mod=1e9+7;
typedef long long ll;

ll dp[maxn];

int main(){
    int t;
    cin>>t;
    int m,n;
    while(t--){
        cin>>n>>m;
        ll ans=2;
        ll now=2;	//表示[max(i-m,0),i-1]区间的和
        int p=0; 	//表示区间左端
        dp[0]=dp[1]=1;
        for(int i=2;i<=n;i++){
            if(i-p>m) now=(now-dp[p++]+mod)%mod;
            dp[i]=now;
            ans=(ans+dp[i])%mod;
            now=(now+dp[i])%mod;
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值