BZOJ 2973 石头游戏 矩乘加速递推

FFFFFFF,看了一上午才看懂,又调了一中午。。。。。我终于明白为何自己如此菜了qwq


这个题加速的思路是:因为每个序列的长度小于6,他们的lcm是60,所以六十次以后就会回到原来的序列。

加速的就是这一个个重复的60次

我们把60个转移矩阵乘起来(结合律),设为d,然后有x=t/60就是有多少个d,算出d的x次方(快速幂)

然后不足60次的一个个乘起来就好了

至于如何建转移矩阵。。。模拟一下吧(我搞了一上午qwq):

e[k]是第k次的转移矩阵,取石子从0里面取(因此e[k][0][0]都是1)

对于这个矩阵,可以理解为 e[第k次][从哪个状态来][到哪个状态去]

因为矩阵乘不就是ret[i][j]+=a[i][k]*b[k][j],其中b就是转移矩阵(再不理解可以吧i那一维给去了)

PS:sizeof时注意是指针的大小还是数组的大小。。。因为这个调了一中午。。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long
#define R register int
using namespace std;
inline ll g() {
register ll ret=,fix=; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-:fix;
do ret=ret*+(ch^); while(isdigit(ch=getchar())); return ret*fix;
}
int n,m,t,p,q;
int a[][],cnt[][];
ll f[],d[][],e[][][];
ll ans;
char b[][],s[],ch;
inline int pos(int i,int j) {return (i-)*m+j;}
inline ll max(ll a,ll b) {return a>b?a:b;}
// inline void print(ll *a) { cout<<endl<<"fasdfas"; //调试输出
// for(R i=0;i<=p;++i) printf("%lld ",a[i]); cout<<endl;
// }
// inline void print2(ll a[70][70]) { cout<<"eeewee";
// for(R i=0;i<=p;++i,cout<<endl<<" ") for(R j=0;j<=p;++j) printf("%lld ",a[i][j]);
// }
inline void mul1(ll a[][],ll b[][]) {
register ll ret[][]; memset(ret,,sizeof(ret));
for(R i=;i<=p;++i) for(R k=;k<=p;++k) if(a[i][k]) for(R j=;j<=p;++j)
ret[i][j]+=a[i][k]*b[k][j];
memcpy(a,ret,sizeof(ret));
}
inline void mul2(ll f[],ll a[][]) {
register ll ret[]; memset(ret,,sizeof(ret));
for(R j=;j<=p;++j) for(R k=;k<=p;++k)
ret[j]+=f[k]*a[k][j];
memcpy(f,ret,sizeof(ret));
}
inline void build() {
for(R k=;k<=;e[k][][]=,++k) for(R i=;i<=n;++i) for(R j=;j<=m;++j) {
R x=a[i][j],p=cnt[i][j];
if(b[x][p]>=''&&b[x][p]<='') {
e[k][][pos(i,j)]=b[x][p]-'';
e[k][pos(i,j)][pos(i,j)]=;
} else if(b[x][p]=='N'&&i>) e[k][pos(i,j)][pos(i-,j)]=;
else if(b[x][p]=='W'&&j>) e[k][pos(i,j)][pos(i,j-)]=;
else if(b[x][p]=='S'&&i<n) e[k][pos(i,j)][pos(i+,j)]=;
else if(b[x][p]=='E'&&j<m) e[k][pos(i,j)][pos(i,j+)]=;
cnt[i][j]=(p+)%strlen(b[x]);
} if(t>) {memcpy(d,e[],sizeof(e[])); for(R i=;i<=;++i) mul1(d,e[i]);}
}
signed main() {
n=g(),m=g(),t=g(),q=g(); p=n*m;
for(R i=;i<=n;++i) {
scanf("%s",s+);
for(R j=;j<=m;++j) ch=s[j],a[i][j]=(ch^)+;
} for(R i=;i<=q;++i) scanf("%s",&b[i]);
build(); f[]=;
for(R i=t/;i;i>>=,mul1(d,d)) if(i&) mul2(f,d);
for(R i=,lim=t%;i<=lim;++i) mul2(f,e[i]);
for(R i=;i<=p;++i) ans=max(ans,f[i]); printf("%lld\n",ans);
//while(1);
}

别颓废,至少看起来你懂了。2019.05.10

上一篇:hdu 1811拓扑排序+并查集(容器实现)


下一篇:dalvik