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***3
与 1***23
,此时三者表示同一个序列 123
,该序列已被第一个分隔区间的子序列 123
贡献,但可以看到还需要对 12
、 3
与 1
、23
产生的重复贡献进行去重,跨区间的去重处理较为麻烦。
思考如何避免该类复杂计算。
注意到这种重复计数产生于连续空缺长度大于等于
m
m
m 的情况。
以此作为突破口,存在递推计数手段。
对于 123123
中的第一个 123
中的 1
来说,后面两个 2
在123***
、12***3
与 1***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(k−m,0)∑k−1dpi (0<k≤n)
那么可以得出答案 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;
}