bzoj 1814: Ural 1519 Formula 1【插頭dp】
阿新 • • 發佈:2018-12-14
設f[i][j][s]為輪廓線推到格子(i,j),狀態為s的方案數
括號表示一段線的左端和右端,表示成左括號和右括號,狀壓的時候用1和2表示,0表示已經閉合
下面的藍線是黃色格子的輪廓線,dp轉移要把它轉到橙色輪廓線,設已經在狀壓的s中取到兩條邊的狀態記為b1,b2
然後分很多情況討論:
(i,j)是障礙:那就只能什麼都不放的轉移,也就是隻能從b1=0,b2=0轉移到新輪廓線的b1=0,b2=0
if(!a[i][j]) { if(!b1&&!b2) add(x,v); }
b1=0,b2=0:因為不能空,所以只能轉移一個拐角
else if(!b1&&!b2)
{
if(a[i+1][j]&&a[i][j+1])
add(x+b[j-1]+2*b[j],v);
}
b1=0或者b2=0:根據有無障礙判斷能不能轉移,如果(i,j+1),(i+1,j)都沒有障礙的話就有兩種轉移,以b1=0,b2!=0為例:
一種是接上然後拐彎,這樣轉移後的輪廓線括號狀態不變
另一種是接上直著走,轉移後的輪廓線括號狀態b1b2互換
b1!=0,b2=0同理
else if(!b1&&b2) { if(a[i][j+1]) add(x,v); if(a[i+1][j]) add(x-b[j]*b2+b[j-1]*b2,v); } else if(b1&&!b2) { if(a[i][j+1]) add(x-b[j-1]*b1+b[j]*b1,v); if(a[i+1][j]) add(x,v); }
b1=b2=1或2:這樣這兩條線會在(i,j)格子連起來,兩隊括號合成一對,以b1=b2=1為例:
else if(b1==1&&b2==1)
{
for(int t=1,l=j+1;l<=m;l++)
{
if((x>>(l*2))%4==1)
t++;
if((x>>(l*2))%4==2)
t--;
if(!t)
{
add(x-b[j]-b[j-1]-b[l],v);
break;
}
}
}
else if(b1==2&&b2==2)
{
for(int t=1,l=j-2;l>=0;l--)
{
if((x>>(l*2))%4==1)
t--;
if((x>>(l*2))%4==2)
t++;
if(!t)
{
add(x+b[l]-2*b[j]-2*b[j-1],v);
break;
}
}
}
b1=2,b2=1:和上面差不多,就是把這兩個括號合併就行了
else if(b1==2&&b2==1)
add(x-2*b[j-1]-b[j],v);
b1=1,b2=2:這個只有到最後一個沒障礙的點才能轉移,因為這是把一條線連成一個迴路的最後一步
其實不用轉移,直接加進答案就行了
else if(i==tx&&j==ty)
ans+=v;
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=15,mod=299989;
int n,m,la,nw,a[N][N],b[N],c[2],h[300005],tx,ty;
long long ans;
char s[N];
struct qwe
{
int ne,to[2];
long long va[2];
}e[300005];
void add(int x,long long v)
{
int u=x%mod+1;
for(int i=h[u];i;i=e[i].ne)
if(e[i].to[nw]==x)
{
e[i].va[nw]+=v;
return;
}
e[++c[nw]].ne=h[u];
e[c[nw]].to[nw]=x;
e[c[nw]].va[nw]=v;
h[u]=c[nw];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
if(s[j]=='.')
a[i][j]=1,tx=i,ty=j;
}
b[0]=1;
for(int i=1;i<=12;i++)
b[i]=b[i-1]<<2;
c[0]=1,e[1].va[0]=1,e[1].to[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c[nw];j++)
e[j].to[nw]<<=2;
for(int j=1;j<=m;j++)
{
la=nw,nw^=1;
memset(h,0,sizeof(h));
c[nw]=0;
for(int k=1;k<=c[la];k++)
{
int x=e[k].to[la],b1=(x>>(j*2-2))%4,b2=(x>>(j*2))%4;
long long v=e[k].va[la];
if(!a[i][j])
{
if(!b1&&!b2)
add(x,v);
}
else if(!b1&&!b2)
{
if(a[i+1][j]&&a[i][j+1])
add(x+b[j-1]+2*b[j],v);
}
else if(!b1&&b2)
{
if(a[i][j+1])
add(x,v);
if(a[i+1][j])
add(x-b[j]*b2+b[j-1]*b2,v);
}
else if(b1&&!b2)
{
if(a[i][j+1])
add(x-b[j-1]*b1+b[j]*b1,v);
if(a[i+1][j])
add(x,v);
}
else if(b1==1&&b2==1)
{
for(int t=1,l=j+1;l<=m;l++)
{
if((x>>(l*2))%4==1)
t++;
if((x>>(l*2))%4==2)
t--;
if(!t)
{
add(x-b[j]-b[j-1]-b[l],v);
break;
}
}
}
else if(b1==2&&b2==2)
{
for(int t=1,l=j-2;l>=0;l--)
{
if((x>>(l*2))%4==1)
t--;
if((x>>(l*2))%4==2)
t++;
if(!t)
{
add(x+b[l]-2*b[j]-2*b[j-1],v);
break;
}
}
}
else if(b1==2&&b2==1)
add(x-2*b[j-1]-b[j],v);
else if(i==tx&&j==ty)
ans+=v;
}
}
}
printf("%lld\n",ans);
return 0;
}