题解:P5678 [GZOI2017] 河神
题目传送门
这题就是给出数列 \(\{a_n\}\) 和 \(\{b_n\}\) 以及 \(\{A_n\}\) 的递推关系, 试求出数列 \(\{A_n\}\) 第 \(N\) 项。
递推关系为。
\[A_n=\begin{cases}a_n & 0 \le n < K \\ \bigoplus (A_{n-K+t} \otimes b_t) & n \ge K \end{cases}
\]
其中,\(\otimes\) 表示与操作,\(\oplus\) 表示或操作。
容易得到,\(A_n\) 只与 \(b_i\) 和 \(A_i\) 的前 \(k\) 项有关。所以我们可以构建一下矩阵(假设 \(k=4\))。
\[\begin{bmatrix}A_i&A_{i+1}&A_{i+2}&A_{i+3}\end{bmatrix}\times\begin{bmatrix}0&0&0&b_1\\-1&0&0&b_2\\0&-1&0&b_3\\0&0&-1&b_4\end{bmatrix}=\begin{bmatrix}A_{i+1}&A_{i+2}&A_{i+3}&A_{i+4}\end{bmatrix}
\]
在这个矩阵里,我们要把原先的结果矩阵第 \(i\) 行 \(j\) 列 \(=\) 第一个矩阵的第 \(i\) 行 \(\times\) 第二个矩阵的第 \(j\) 列再相加,把 \(\times\) 改为按位与,把相加改为按位或就可以了。
那么最终的答案如下(以 \(k=4\) 为例)。
\[\begin{bmatrix}a_1&a_2&a_3&a_4\end{bmatrix}\times\begin{bmatrix}0&0&0&b_1\\-1&0&0&b_2\\0&-1&0&b_3\\0&0&-1&b_4\end{bmatrix}^{n-4}=\begin{bmatrix}A_{n-3}&A_{n-2}&A_{n-1}&A_{n}\end{bmatrix}
\]
所以代码也就很好打了,这里解释一下为什么第二的矩阵里面是 \(-1\) 而不是 \(1\),这是因为是要进行与运算,而一个数与上 \(-1\) 还等于他自己,所以用 \(-1\)。而 \(0\) 是因为任何数或 \(0\) 等于他自己。
代码。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=101,N=1010101;
const int mod=1e4;
ll n,m,s,t,k,num,w[N],T,a[N],b[N];
struct tt{
ll a[M][M];
tt(){memset(a,0,sizeof(a));}
tt operator*(const tt &b)const{
tt res;
for(int i=1;i<=k;i++)
for(int j=1;j<=k;j++)
for(int l=1;l<=k;l++)
res.a[i][j]=(res.a[i][j]|(a[i][l]&b.a[l][j]));
return res;
}
}ans,base,st;
void qpow(tt base,ll y){
while(y){
if(y&1)ans=ans*base;
base=base*base;
y>>=1;
}
}
int main(){
scanf("%lld%lld",&n,&k);
n++;
for(int i=1;i<=k;i++)scanf("%lld",&a[i]),ans.a[1][i]=a[i];
for(int i=1;i<=k;i++)scanf("%lld",&b[i]);
for(int i=1;i<=k;i++)base.a[i][k]=b[i],base.a[i+1][i]=-1;
if(n<=k){
printf("%lld",a[n]);
return 0;
}
qpow(base,n-k);
printf("%lld",ans.a[1][k]);
return 0;
}

浙公网安备 33010602011771号