题面
分析
很吼的一题。
看到1e16那肯定是矩阵乘法
考虑设状态
f[i][j]
表示1..i,现在有恰好连续j个颜色不一样的。
如何转移?
首先是到j+1的,只要他后面有j个不同,那么都有(m-j)种放法转移到
f[i+1][j+1]
然后是重复的,假设在第x个位置与当前放的有重复,那么重复的方案是多少呢?
考虑每一种确定的方案,他都有恰好一种放法能转移到
f[i+1][1..j]
所以这个转移就恰好是
f[i][j]−>f[i+1][1..j]
直接套个矩阵乘法优化就OK了,注意矩阵乘法不要打得太浪费资源。 不过该传参还是得传参,该返回还是得返回,不要因小失大了。 (最好能测一下极限 这种题的话)
Demo
#include <cstdio>
#include <iostream>
#include <cstring>
#define add(x,y) (x=(x+y)%mo)
#define copy(x,y) memcpy(x.h,y.h,sizeof y.h)
using namespace std;
typedef long long ll;
const int mo=1e9+7,M=105;
struct mat{
ll h[M][M];
ll * operator[](int x) {return h[x];}
void clear() {memset(h,0,sizeof h);}
} omat,f,tmp,c,rzksm,rzmu;
ll n,m;
void mult(mat &a,mat &b) {
rzmu.clear();
for (int i=1; i<=m+1; i++)
for (int j=1; j<=m+1; j++)
for (int k=1; k<=m+1; k++) add(rzmu[i][j],a.h[i][k]*b.h[k][j]);
}
void ksm(mat &x,ll y) {
if (y==1) {
copy(rzksm,x);
return;
}
ksm(x,y>>1);
mult(rzksm,rzksm);
copy(rzksm,rzmu);
if ((y&1)==1) mult(rzksm,x),copy(rzksm,rzmu);
}
int main() {
freopen("2.in","r",stdin);
cin>>n>>m;
for (int i=1; i<m; i++) {
omat.h[i][i+1]=m-i;
for (int j=1; j<=i; j++) omat.h[i][j]=1;
}
f.h[1][1]=m;
ksm(omat,n-1);
mult(f,rzksm);
ll ans=0;
for (int i=1; i<m; i++) add(ans, rzmu.h[1][i]);
printf("%lld\n",ans);
}