1. 程式人生 > >BZOJ 2973 石頭遊戲 矩乘加速遞推

BZOJ 2973 石頭遊戲 矩乘加速遞推

sign bzoj name ++i char urn get pri lse

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=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==-?-1:fix;
    
do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } int n,m,t,p,q; int a[20][20],cnt[20][20]; ll f[70],d[70][70],e[70][70][70]; ll ans; char b[20][20],s[100],ch; inline int pos(int i,int j) {return (i-1)*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[70][70],ll b[70][70]) { register ll ret[70][70]; memset(ret,0,sizeof(ret)); for(R i=0;i<=p;++i) for(R k=0;k<=p;++k) if(a[i][k]) for(R j=0;j<=p;++j) ret[i][j]+=a[i][k]*b[k][j]; memcpy(a,ret,sizeof(ret)); } inline void mul2(ll f[70],ll a[70][70]) { register ll ret[70]; memset(ret,0,sizeof(ret)); for(R j=0;j<=p;++j) for(R k=0;k<=p;++k) ret[j]+=f[k]*a[k][j]; memcpy(f,ret,sizeof(ret)); } inline void build() { for(R k=1;k<=60;e[k][0][0]=1,++k) for(R i=1;i<=n;++i) for(R j=1;j<=m;++j) { R x=a[i][j],p=cnt[i][j]; if(b[x][p]>=0&&b[x][p]<=9) { e[k][0][pos(i,j)]=b[x][p]-0; e[k][pos(i,j)][pos(i,j)]=1; } else if(b[x][p]==N&&i>1) e[k][pos(i,j)][pos(i-1,j)]=1; else if(b[x][p]==W&&j>1) e[k][pos(i,j)][pos(i,j-1)]=1; else if(b[x][p]==S&&i<n) e[k][pos(i,j)][pos(i+1,j)]=1; else if(b[x][p]==E&&j<m) e[k][pos(i,j)][pos(i,j+1)]=1; cnt[i][j]=(p+1)%strlen(b[x]); } if(t>60) {memcpy(d,e[1],sizeof(e[1])); for(R i=2;i<=60;++i) mul1(d,e[i]);} } signed main() { n=g(),m=g(),t=g(),q=g(); p=n*m; for(R i=1;i<=n;++i) { scanf("%s",s+1); for(R j=1;j<=m;++j) ch=s[j],a[i][j]=(ch^48)+1; } for(R i=1;i<=q;++i) scanf("%s",&b[i]); build(); f[0]=1; for(R i=t/60;i;i>>=1,mul1(d,d)) if(i&1) mul2(f,d); for(R i=1,lim=t%60;i<=lim;++i) mul2(f,e[i]); for(R i=1;i<=p;++i) ans=max(ans,f[i]); printf("%lld\n",ans); //while(1); }

別頹廢,至少看起來你懂了。2019.05.10

BZOJ 2973 石頭遊戲 矩乘加速遞推