Dungeon Creation (AOJ2393)
H*Wのグリッドで各マスは白か黒で塗られている.H*(W-1)+(H-1)*W個の壁それぞれを立てるか立てないか決めて,全体が連結でサイクルが無いようにする方法は何通りか.
迷路→全域木→行列木定理 O(N^3) (N=H*W=7500) だし無理
W=15だしbitDP?→上から埋めてくと実は連結状況は(1-3,2-4みたいに交差しないという条件から)カタラン数 通りになるが,それでもでかすぎるから無理.
→は???
実は行列木定理でした
幅Bの帯行列(band matrix)とは,a[x][y]!=0⇒abs(x-y)<=Bをみたすもの.普通の正方行列は幅N-1とみなせる.何が嬉しいかというと,det求めるのがtime O(NB^2),space O(NB) で出来る.
掃き出し法をやる時に,各行のループ内で,掃き出さなきゃいけない行がO(B)でその時に見る列の個数もO(B)なので,行列をうまく持てば上のような計算量で行ける.
今回のグラフは普通にマス目に順番をつければ幅がWになるので勝利.
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<(int)(n);i++) #define rep1(i,n) for(int i=1;i<=(int)(n);i++) #define all(c) c.begin(),c.end() #define pb push_back #define fs first #define sc second #define show(x) cout << #x << " = " << x << endl #define chmin(x,y) x=min(x,y) #define chmax(x,y) x=max(x,y) using namespace std; /* use under a[i][j]!=0 -> abs(i-j)<=B (高々+-Bにしか辺が貼られてない) 掃き出し時のswapの影響で,-B~+2Bまでの3B+1個をviの中に持ってる 適当に0をつめてB-th value(0-indexed) がa[i][i]になるようにしている time O(NB^2) space O(NB) */ typedef long long ll; ll mod=1e9+7; typedef vector<ll> vi; typedef vector<vi> bmat; ll ex(ll x,ll p){ ll a=1; while(p){ if(p%2) a=a*x%mod; x=x*x%mod; p/=2; } return a; } ll inv(ll a){ return ex(a,mod-2); } ll det(bmat a){ int N=a.size(); if(N==0) return 1; int B=a[0].size()/3; ll ans=1; rep(i,N){ if(a[i][B]==0){ for(int j=i+1;j<N;j++){ if(B-(j-i)<0) break; if(a[j][B-(j-i)]!=0){ int d=j-i; for(int k=d;k<=2*B+d;k++){ swap(a[i][k],a[j][k-d]); } ans*=-1; break; } } if(a[i][B]==0) return 0; } for(int j=i+1;j<N;j++){ if(B-(j-i)<0) break; int d=j-i; ll c=a[j][B-d]*inv(a[i][B])%mod; for(int k=B;k<=3*B;k++){ a[j][k-d]=(a[j][k-d]-c*a[i][k])%mod; } } ans=ans*a[i][B]%mod; } if(ans<0) ans+=mod; return ans; } int H,W; int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1}; string s[500]; bool is(int x,int y){ return 0<=x&&x<H&&0<=y&&y<W&&s[x][y]=='.'; } int n[500][15]; int main(){ int tt=0; while(true){ tt++; cin>>H>>W; if(H==0) break; rep(i,H) cin>>s[i]; int N=0; rep(i,H) rep(j,W){ if(s[i][j]=='#') n[i][j]=-1; else n[i][j]=N++; } bmat a(N-1,vi(3*W+1,0)); rep(i,H) rep(j,W){ if(!is(i,j)) continue; if(n[i][j]==N-1) break; rep(d,4){ int x=i+dx[d],y=j+dy[d]; if(!is(x,y)) continue; if(n[x][y]!=N-1) a[n[i][j]][n[x][y]-n[i][j]+W]++; a[n[i][j]][W]++; } } cout<<"Case "<<tt<<": "<<det(a)<<endl; } }