题解 P5303 【[GXOI/GZOI2019]逼死强迫症】
大佬们的做法都太神啦,我这个菜鸡只会暴力 dp。
首先我们发现题目这个东西不好设状态,所以我们先做一个简单的容斥,将所有方案数量减去不合法(即两块单砖挨在一起了)的数量。
不合法的数量是多少呢?我们发现两块砖既然挨在一起了,就可以直接看作是一块大的砖,所以就是斐波那契数列。但是这里由于每一块大砖都可以变成两块小砖,所以实际上要乘上
对于所有方案的我们暴力 dp。设
这个算法的时间复杂度是 还是那句话,因为
下面是奇丑无比的代码,使用了 C++11 的写法减少码量,在提交后成功拿到全谷最劣解。
#include<vector>
#include<cctype>
#include<cstdio>
using namespace std;
inline int readint(){
int x=0;
bool f=0;
char c=getchar();
while(!isdigit(c)&&c!='-') c=getchar();
if(c=='-'){
f=1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return f?-x:x;
}
typedef long long ll;
const ll mod=1e9+7;
typedef vector<ll> col;
typedef vector<col> matrix;
matrix operator *(matrix a,matrix b){
int n=a.size();
matrix c;
for(int i=0;i<n;i++){
c.push_back(col());
for(int j=0;j<n;j++) c[i].push_back(0);
}
for(int i=0;i<n;i++) for(int j=0;j<n;j++)
for(int k=0;k<n;k++) c[i][j]=(c[i][j]+a[i][k]*b[k][j]%mod)%mod;
return c;
}
matrix ksm(matrix a,ll b){
matrix ans;
int n=a.size();
for(int i=0;i<n;i++){
ans.push_back(col());
for(int j=0;j<n;j++) ans[i].push_back(0);
}
for(int i=0;i<n;i++) ans[i][i]=1;
while(b>0){
if(b%2==1) ans=ans*a;
a=a*a;
b/=2;
}
return ans;
}
const matrix m1={{0,1},{1,1}},
m2={
{1,0,1,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0},
{0,2,0,1,0,1,0,0,0},
{1,0,0,0,1,0,0,0,0},
{0,0,0,1,0,0,0,0,0},
{1,0,0,0,2,0,1,0,1},
{0,0,0,1,0,0,0,1,0},
{0,0,0,0,0,0,1,0,0}
};
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int t=readint();
while(t--){
int n=readint();
ll ans=ksm(m2,n)[6][0];
matrix res=ksm(m1,n);
ans=(ans+mod-(res[0][0]+res[0][1])%mod*n%mod)%mod;
printf("%d\n",(int)ans);
}
return 0;
}
这个算法其实是有优化的余地的,比如