AT_abc258_h [ABC258Ex] Odd Steps 题解
题目没有要求 \(X\) 的元素个数之类的,导致 \(X\) 的限制非常少,注意到“限制是性质”,这也意味着 \(X\) 的性质非常少,我们不好下手,转而看 \(A\) 。非常自然的想法就是容斥dp,把 \(S\) 放到 \(A\) 的最后一个元素,令 \(dp_i\) 表示目前 \(X\) 的和等于 \(A_i\) ,\(i\) 之前的限制都满足的方案数,则转移方程为 $$dp_i=F_{A_i}-\sum{dp_j*F_{A_i-A_j}}$$ 其中 \(F_i\) 表示 \(i\) 的奇数拆分数,打表发现 \(F_i\) 恰好就是斐波那契数列。
证明:构建一个奇数数列使其和为 \(i\) ,可以使用以下两种操作
1.在数列末尾新加入一个 \(1\)
2.令数列末尾的数加上 \(2\)
于是 \(F_i=F_{i-1}+F_{i-2}\) ,刚好满足斐波那契的形式
所以 \(F\) 数组就可以直接矩阵快速幂求出,至于 \(dp\) 数组,同样使用矩阵快速幂。
我们枚举到 \(i-1\) 时设一个向量
\[V=\begin{pmatrix}
\sum{dp_{i-1}*F_{A_{i-1}-A_j}} &\sum{dp_{i-1}*F_{A_{i-1}-A_j-1}} )
\end{pmatrix}\]
然后用和 \(F\) 一样的递推方式进行递推,每次从 \(i-1\) 到 \(i\) ,就乘 \(d=A_i-A_{i-1}\) 次转移矩阵,算出dp值后再把当前 \(i\) 加到 \(V\) 里即可,最后输出 \(ans=dp_{n+1}\) 。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int Mod=998244353;
const int N=1e5+5;
int n;
ll S;
ll A[N];
inline void add(int &x,int y){
x+=y;
if(x>=Mod) x-=Mod;
}
struct Matrix{
int a[2][2];
Matrix operator *(const Matrix &B)const{
Matrix res={};
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
add(res.a[i][j],(ll)a[i][k]*B.a[k][j]%Mod);
}
}
}
return res;
}
}init,p,V;
inline Matrix ksm(Matrix x,ll y){
Matrix res={};
res.a[0][0]=res.a[1][1]=1;
while(y){
if(y&1) res=res*x;
y>>=1;
x=x*x;
}
return res;
}
inline int fb(ll x){
if(x==0) return 0;
if(x==-1) return 1;
return (init*ksm(p,x-1)).a[0][1];
}
ll dp[N];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>S;
for(int i=1;i<=n;i++) cin>>A[i];
A[++n]=S;
init.a[0][1]=1;
p.a[0][1]=p.a[1][0]=p.a[1][1]=1;
dp[1]=fb(A[1]);
V.a[0][0]=dp[1];
for(int i=2;i<=n;i++){
V=V*ksm(p,A[i]-A[i-1]);
dp[i]=(fb(A[i])-V.a[0][1]+Mod)%Mod;
add(V.a[0][0],dp[i]);
}
cout<<dp[n]<<'\n';
return 0;
}

浙公网安备 33010602011771号