牛客网暑期ACM多校训练营(第二场) 题解 A run 递推 dp
链接:https://www.nowcoder.com/acm/contest/140/A
来源:牛客网
White Cloud is exercising in the playground.
White Cloud can walk 1 meters or run k meters per second.
Since White Cloud is tired,it can't run for two or more continuous seconds.
White Cloud will move L to R meters. It wants to know how many different ways there are to achieve its goal.
Two ways are different if and only if they move different meters or spend different seconds or in one second, one of them walks and the other runs.
White Cloud can walk 1 meters or run k meters per second.
Since White Cloud is tired,it can't run for two or more continuous seconds.
White Cloud will move L to R meters. It wants to know how many different ways there are to achieve its goal.
Two ways are different if and only if they move different meters or spend different seconds or in one second, one of them walks and the other runs.
输入描述:
The first line of input contains 2 integers Q and k.Q is the number of queries.(Q<=100000,2<=k<=100000)
For the next Q lines,each line contains two integers L and R.(1<=L<=R<=100000)
输出描述:
For each query,print a line which contains an integer,denoting the answer of the query modulo 1000000007.
分析:题目让我们求从0到(l,r)的所有可能走法,每次可以走1或者k,但是走了k必须停一次
从0到x(1<=x<=n)每次走1或者k,不考虑停一次的种数符合递推式:
f(x) = f(x-1) + f(x-k)(x>=k) f(x) = 1(x>=1) f(0) = 0(x=0)
比赛时我是看其中k=2时符合斐波拉数列,于是想到了递推,然后推了3写出了递推式
然后我们看连续走k的情况,可以发现:
g(x) = 0(x<2*k) g(x) = x-k+1(2*k<=x<=3*k-1) g(x) = g(x-1) + g(x-k+1) (x>=3*k)
最后到x符合情况的种数就是:f(x)-g(x)
而每次要求到(l,r),我们可以先算个前缀和,在开始打表出所有值就行
当然k=1的时候,因为走和跑不同,所以不是每种都为1,计算一下可以发现符合斐波拉数列(可以考虑把走看成1,跑看成0,然后求在1中间插0的排列数)
#include <map> #include <set> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #define debug(a) cout << #a << " " << a << endl using namespace std; const int maxn = 1e5; const int mod = 1e9 + 7; typedef long long ll; ll a[10*maxn+10], sum[10*maxn+10], b[10*maxn+10]; int main() { std::ios::sync_with_stdio(false); ll n, k; cin >> n >> k; if( k != 1 ) { sum[0] = 0; for( ll i = 1; i <= k-1; i ++ ) { a[i] = 1; sum[i] = sum[i-1] + a[i]; } a[k] = 2; sum[k] = sum[k-1] + a[k]; ll num = 1; for( ll i = 2*k; i <= 3*k-1; i ++ ) { b[i] = num; num ++; } for( ll i = k+1; i <= maxn; i ++ ) { a[i] = ( a[i-1] + a[i-k] ) % mod; if( i >= 3*k ) { b[i] = ( b[i-1] + b[i-k+1] ) % mod; sum[i] = ( sum[i-1] + ( a[i] - b[i] ) % mod ) % mod; } else if( i < 3*k && i >= 2*k ) { sum[i] = ( sum[i-1] + ( a[i] - b[i] ) % mod ) % mod; } else { sum[i] = ( sum[i-1] + a[i] ) % mod; } } } else { sum[1] = 2, sum[2] = 5; a[1] = 2, a[2] = 3; for( ll i = 3; i <= maxn; i ++ ) { a[i] = ( a[i-1] + a[i-2] ) % mod; sum[i] = ( sum[i-1] + a[i] ) % mod; } } while( n -- ) { ll le, ri; cin >> le >> ri; if( le == 1 ) { cout << sum[ri] << endl; } else { cout << ( sum[ri] - sum[le-1] + mod ) % mod << endl; } } return 0; }
再贴一个dp的做法
f[i][0/1]表示已经跑了i米,最后一步是跑还是走的方案数。 f[i][1]=f[i-k][0],f[i][0]=f[i-1][0]+f[i-1][1] 答案即为l到r的f[i][0]和f[i][1]的累加 ,我们只需记录前缀和即可
#include <map> #include <set> #include <stack> #include <cmath> #include <queue> #include <cstdio> #include <vector> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #define debug(a) cout << #a << " " << a << endl using namespace std; const int maxn = 1e5; const int mod = 1e9 + 7; typedef long long ll; ll sum[2*maxn], dp[2*maxn][3]; int main() { std::ios::sync_with_stdio(false); ll n, k, le, ri; cin >> n >> k; memset( dp, 0, sizeof(dp) ); memset( sum, 0, sizeof(sum) ); sum[0] = 0, dp[0][0] = 0, dp[0][1] = 0; for( ll i = 1; i <= k; i ++ ) { dp[i][0] = 1; if( i == k ) { dp[k][1] = 1; } sum[i] = ( dp[i][0] + dp[i][1] + sum[i-1] ) % mod; } for( ll i = k+1; i <= maxn; i ++ ) { dp[i][0] = dp[i-1][0] + dp[i-1][1], dp[i][1] = dp[i-k][0]; sum[i] = ( sum[i-1] + dp[i][1] + dp[i][0] ) % mod; } while( n -- ) { cin >> le >> ri; cout << ( sum[ri] - sum[le-1] + mod ) % mod << endl; } return 0; }
彼时当年少,莫负好时光。