BZOJ5207 : [Jsoi2017]隧道
若$\min(n,m)<\min(n+1,m-1)$,则考虑计算左边与右边不连通的概率,然后用$1$减去它得到答案。
若$\min(n,m)\geq \min(n+1,m-1)$,则考虑计算对偶图中上边与下边不连通的概率。
同时当$n<m$时还可以旋转$90$°来交换$n$和$m$,使得$m\leq 9$。
那么问题转化为,给定$n\times m$的网格图,每个点颜色为$0,1,2$,每条边有存在的概率,求每个连通块不同时含有颜色$1$和$2$的概率。
连通性状压DP,设$f[i][j][S]$表示考虑到了$(i,j)$,轮廓线状态为$S$的概率。
其中$S$记录轮廓线上$m$个点所属连通块的最小表示,以及每个连通块的颜色。
为了避免map的使用,可以考虑扩展出所有状态后再进行排序,将相同状态的DP值合并,减小常数。
同时可以将$1$和$2$内部的边存在的概率设置为$1$,减小连通块的数量已达到优化状态数的效果。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=115,M=1000000,P=1000000007; int n,m,o,i,j,k,_id[N][N],_wr[N][N],_wd[N][N],id[N][N],wr[N][N],wd[N][N]; struct E{ ll x;int y,v; E(){} E(ll _x,int _y,int _v){x=_x,y=_y,v=_v;} }e[2][M]; int cnt[2]; inline bool cmp(const E&a,const E&b){ if(a.x!=b.x)return a.x<b.x; return a.y<b.y; } inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;} inline int po(int a,int b){int t=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P;return t;} inline int read(){ int x; scanf("%d",&x); return 1LL*x*po(100,P-2)%P; } inline int read2(){ int x=read(); x=(P+1-x)%P; return x; } inline void fix(){ int m=cnt[o],t=0,i,j,k; sort(e[o],e[o]+m,cmp); for(i=0;i<m;i=j){ for(k=0,j=i;j<m&&e[o][i].x==e[o][j].x&&e[o][i].y==e[o][j].y;j++)up(k,e[o][j].v); if(k)e[o^1][t++]=E(e[o][i].x,e[o][i].y,k); } cnt[o^=1]=t; } inline ll get(ll x,int y){return x>>(y<<2)&15;} inline void cal(ll&A,int&B){ static int v[N],f[N]; int i,x,t=0,C=0; for(i=0;i<=m;i++)v[i]=-1; for(t=i=0;i<m;i++){ x=get(A,i); if(v[x]==-1){ C|=(B>>(x<<1)&3)<<(t<<1); v[x]=t++; } f[i]=v[x]; } for(A=0,B=C,i=m-1;~i;i--)A=A<<4|f[i]; } inline void up0(ll A,int B,int C,int x,int y){ A^=(get(A,x)^m)<<(x<<2); B|=y<<(m<<1); cal(A,B); e[o^1][cnt[o^1]++]=E(A,B,C); } inline void upl(ll A,int B,int C,int x,int y){ int t=get(A,x-1); if(y+(B>>(t<<1)&3)==3)return; A^=(get(A,x)^t)<<(x<<2); B|=y<<(t<<1); cal(A,B); e[o^1][cnt[o^1]++]=E(A,B,C); } inline void upu(ll A,int B,int C,int x,int y){ int t=get(A,x); if(y+(B>>(t<<1)&3)==3)return; B|=y<<(t<<1); e[o^1][cnt[o^1]++]=E(A,B,C); } inline void upul(ll A,int B,int C,int x,int y){ int t=get(A,x),z=get(A,x-1); if(y+(B>>(t<<1)&3)==3)return; B|=y<<(t<<1); if((B>>(z<<1)&3)+(B>>(t<<1)&3)==3)return; B|=(B>>(z<<1)&3)<<(t<<1); ll S=z^t; for(int i=0;i<m;i++)if(get(A,i)==z)A^=S<<(i<<2); cal(A,B); e[o^1][cnt[o^1]++]=E(A,B,C); } int solve(){ if(n<m){ for(i=1;i<=n;i++)for(j=1;j<m;j++)wd[j][i]=_wr[i][j]; for(i=1;i<n;i++)for(j=1;j<=m;j++)wr[j][i]=_wd[i][j]; for(i=1;i<=n;i++)for(j=1;j<=m;j++)id[j][i]=_id[i][j]; swap(n,m); }else{ for(i=1;i<=n;i++)for(j=1;j<=m;j++)wd[i][j]=_wd[i][j],wr[i][j]=_wr[i][j],id[i][j]=_id[i][j]; } cnt[o=0]=1; e[0][0]=E(0,0,1); for(i=m-1;~i;i--)e[0][0].x=e[0][0].x<<4|i; for(i=1;i<=n;i++)for(j=1;j<=m;j++){ int pl=j>1?wr[i][j-1]:0,npl=(P+1-pl)%P, pu=i>1?wd[i-1][j]:0,npu=(P+1-pu)%P, pul=1LL*pl*pu%P,p0=1LL*npl*npu%P,t=id[i][j]; pl=1LL*pl*npu%P; pu=1LL*pu*npl%P; cnt[o^1]=0; for(k=0;k<cnt[o];k++){ if(pl)upl(e[o][k].x,e[o][k].y,1LL*e[o][k].v*pl%P,j-1,t); if(pu)upu(e[o][k].x,e[o][k].y,1LL*e[o][k].v*pu%P,j-1,t); if(pul)upul(e[o][k].x,e[o][k].y,1LL*e[o][k].v*pul%P,j-1,t); if(p0)up0(e[o][k].x,e[o][k].y,1LL*e[o][k].v*p0%P,j-1,t); } o^=1; fix(); } int ret=0; for(i=0;i<cnt[o];i++)up(ret,e[o][i].v); return ret; } int main(){ int r,c,flag=0; scanf("%d%d",&r,&c); if(c<=1)return puts("1"),0; if(min(r,c)<min(r+1,c-1)){ flag=1; for(i=1;i<=r;i++)for(j=1;j<c;j++)_wr[i][j]=read(); for(i=1;i<r;i++)for(j=2;j<c;j++)_wd[i][j]=read(); for(i=1;i<r;i++)_wd[i][1]=_wd[i][c]=1; for(i=1;i<=r;i++)_id[i][1]=1,_id[i][c]=2; n=r,m=c; }else{ for(i=1;i<=r;i++)for(j=1;j<c;j++)_wd[i][j]=read2(); for(i=1;i<r;i++)for(j=2;j<c;j++)_wr[i+1][j-1]=read2(); for(i=1;i<c-1;i++)_wr[1][i]=_wr[r+1][i]=1; for(i=1;i<c;i++)_id[1][i]=1,_id[r+1][i]=2; n=r+1,m=c-1; } int ans=solve(); if(flag)ans=(P+1-ans)%P; return printf("%d",ans),0; }