「CSA49」Bunny on Number Line

题目大意:有一个人从0开始走,每次可以向前走一步或者回到1,那么会产生一个位置序列,其中给出 \(k\) 个位置是好的。定义一个位置序列是好的,当前仅当其有恰好 \(n\) 个位置是好的,且最后一个位置是好的,相邻两个好的位置距离不超过 \(m\) ,求本质不同的好的序列的长度之和。两个序列本质不同当且仅当存在一个位置满足在其中一个序列中上的位置是好的,另外一个不是。

解题思路:问题转换一下,将好的位置看作 1 ,其它的位置看作 0,组成一个01字符串。那么每次往后走到一个好的位置,相当于往后接一个以1结尾的前缀,然后求这样所得的所有不同的字符串的长度之和。实际上所有1和0都是一样的,区分字符串只有每个1前面的0的数量不同。于是可以重新定义字符 \(b_i =a_i-a_{i-1}\) 。问题转化为求出所有由 \(b\) 组成的合法的不同字符串的长度之和,事实上求长度之和要用到方案数,而方案数会求了长度之和自然就能求了,所以下面就讲方案数怎么求。

考虑一个朴素的做法,令 \(dp(i,j)\) 表示当前长度为 \(i\) 的字符串末尾的字符是 \(b_j\) 的方案数,考虑 \(dp(i,j)\) 向后转移本质上是原串在某一时刻向后加了一个以 \(b_j\) 结尾的前缀,然后转移到这个前缀后面的那个字符。且这个前缀还必须与当前确定的串的后缀匹配,不然就会产生不合法的方案数。分析一下会发现这是一个kmp不断跳失配指针的过程,直接跳转移即可。不过还有几个情况要讨论一下,如果出现之前长度较长的前缀已经能转移到某个 \(c\) ,当前也要转移到 \(c\) ,此时会发现转移后的串本质是一样的,只是 \(j\) 的大小不一样,而由于长的串跳失配指针一定能包含短的串,所以只转移到较长的那个即可。另外 \(b_1\) 是一个通配符可以匹配 \([a_1,m]\) 之间的所有字符,因为其可以不断跳若干次1再走到第一个好的位置,所以要对与 \(b_1\) 的匹配加以特判。

然后这个转移求完之后同理可以利用方案数来求总长度,只需要对每个字符加权计算即可,\(b_1\) 的权是 \(\sum_{i=a_1}^mi\) ,最后矩阵快速幂优化一下这个转移即可,总复杂度 \(O(k^3logn)\) ,用 BM 可以做到 \(O(k^2)\) 。

code

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T & x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
} #define int ll
#define fi first
#define se second const int N = 105, mod = 1e9+7; map<int, int> mp;
int nxt[N], a[N], b[N], n, m, k, ans;
inline void add(int &x, int y){ x = x + y >= mod ? x + y - mod : x + y; }
inline void del(int &x, int y){ x = x - y < 0 ? x - y + mod : x - y; } struct Matrix{
pair<int, int> a[N][N];
inline Matrix(){ memset(a, 0, sizeof(a)); }
inline Matrix operator * (const Matrix &B) const{
Matrix ans;
for(int i = 1; i <= k; i++)
for(int j = 1; j <= k; j++)
for(int p = 1; p <= k; p++){
add(ans.a[i][j].fi, a[i][p].fi * B.a[p][j].fi % mod);
add(ans.a[i][j].se, a[i][p].fi * B.a[p][j].se % mod);
add(ans.a[i][j].se, a[i][p].se * B.a[p][j].fi % mod);
}
return ans;
}
}A, B; inline Matrix Pow(Matrix A, int b){
Matrix ans = A; b--;
for(; b; b >>= 1, A = A * A)
if(b & 1) ans = ans * A;
return ans;
} inline int same(int x, int y){
return y == 1 ? (b[x] >= a[1]) : (b[x] == b[y]);
}
signed main(){
read(k), read(m), read(n), nxt[0] = -1;
for(int i = 1; i <= k; i++) read(a[i]), b[i] = a[i] - a[i-1];
for(int i = 2, j = 0; i <= k; nxt[i++] = ++j)
while(~j && !same(i, j + 1)) j = nxt[j];
int tot = (m - a[1] + 1), sum = tot * (a[1] + m) / 2 % mod;
for(int i = 1; i <= k; i++){
mp.clear();
for(int p = i; p; p = nxt[p]) if(!mp[b[p+1]]){
A.a[p+1][i] = make_pair(1ll, b[p+1]);
if(b[p+1] >= a[1])
del(A.a[1][i].fi, 1), del(A.a[1][i].se, b[p+1]);
mp[b[p+1]] = 1;
}
add(A.a[1][i].fi, tot), add(A.a[1][i].se, sum);
}
if(n == 1) return cout << sum, 0;
B.a[1][1] = make_pair(tot, sum), A = Pow(A, n - 1) * B;
for(int i = 1; i <= k; i++) add(ans, A.a[i][1].se);
cout << ans;
}
05-13 18:26