记录编号 548042 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [Ural 1519] 一级方程式赛车 最终得分 100
用户昵称 Gravatar瑆の時間~無盡輪迴·林蔭 是否通过 通过
代码语言 C++ 运行时间 0.295 s
提交时间 2019-12-31 12:36:18 内存使用 20.95 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
int head[300001],nxt[2<<16],que[2][2<<16],val[2][2<<16],cnt[2],inc[15];
const int maxn=15;
const int HS=299987;
int n,m,ex,ey,now,last,ans;
int map[maxn][maxn];
char S;
void init()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>S;
			if(S=='.')
			{
				map[i][j]=1;
				ex=i,ey=j;
			}
		}
	}
	inc[0]=1;
	for(int i=1;i<=12;i++)
	{
		inc[i]=inc[i-1]*4;
	}
}
void INSERT(int ids,int num)
{
	int U=ids%HS+1;//保证状态哈希值不为0
	for(int i=head[U];i;i=nxt[i])
	{
		if(que[now][i]==ids)
		{
			//是这种状态
			val[now][i]+=num;
			return; 
		}
	}
	//没有出现过 
	cnt[now]++;
	nxt[cnt[now]]=head[U];
	head[U]=cnt[now];
	que[now][cnt[now]]=ids;
	val[now][cnt[now]]=num;
}
void WORK()
{	
	//对于一个格子J,从J向下的插头的贡献是inc[j-1],向右的贡献是inc[j] 
	cnt[now]=1;
	que[now][1]=0;//不向下一行遗传一个插头的初始状态 
	val[now][1]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=cnt[now];j++)
			que[now][j]<<=2;//全部删去来自上一行末尾的右插头,并给这一行第一个加上一个虚拟插头(不存在)
			//因为这项操作是一行进行一次,所以可以保证,被删去的一定是来自上一行末尾的右插头
		for(int j=1;j<=m;j++)
		{
			last=now;
			now^=1;
			memset(head,0,sizeof(head));
			cnt[now]=0; 
			for(int k=1;k<=cnt[last];k++)
			{
				int bit=que[last][k],num=val[last][k];
				int b1=(bit>>((j-1)*2))%4;//J格的右插头存在于J-1的位置;
				int b2=(bit>>(j*2))%4;//J格子的下插头存在于J位置;
				if(!map[i][j])
				{
					if(!b1&&!b2)
					{
						//障碍点且没有点需要用插头通过它进行联通
						INSERT(bit,num); 
					}
					//否则一定不合法 
				} 
				else
				{
					if(!b1&&!b2)
					{
						//如果这个点必须要走且目前在轮廓线上方不与任何点相连
						//那么必须要和下方点和右方点均相连
						if(map[i][j+1]&&map[i+1][j])
							INSERT(bit+inc[j-1]+2*inc[j],num);
						//下方点位置是j-1,右方点是j 
						//因为转移过后已经开始对格子J+1进行操作了。
						//此时状态代表的是J+1 
					}
					else
					{
						if(!b1&&b2)
						{
							//有一个从上面插到这个点的插头
							//可以选择向右出去或者向下出去,只能选一个
							//但是不管怎样,插头的括号性质不变 
							if(map[i][j+1])
								INSERT(bit-inc[j]*b2+inc[j]*b2,num);//先减去上面插头的贡献,再算上向右的插头的贡献 
							if(map[i+1][j])
								INSERT(bit-inc[j]*b2+inc[j-1]*b2,num);//先消去上面插头的贡献,再算向下插头的贡献 
						}
						else
						{
							if(b1&&!b2)
							{
								//大体同上,有一个从左面插到这个点的插头(这个点的右插头)
								//也是可以选择向右或者向下
								//插头性质仍然不变
								if(map[i+1][j])
								{
									INSERT(bit-inc[j-1]*b1+inc[j-1]*b1,num);//先减去右插头的贡献,然后对下面状态后推 
								} 
								if(map[i][j+1])
									INSERT(bit-inc[j-1]*b1+inc[j]*b1,num); 
							}
							else
							{
								if(b1==1&&b2==1)
								{
									int kel=1;
									//两者都是左括号,但是能配对,消去贡献,但是要对j+1即以后的节点进行检查,把第一个找到的多出来的右括号改成左括号
									for(int l=j+1;l<=m;l++)
									{
										int seds=(bit>>(l*2))%4;
										if(seds==1)
											kel++;
										if(seds==2)
											kel--;
										if(kel==0)
										{
											INSERT(bit-inc[j-1]-inc[j]-inc[l],num);//将两个配对的括号消去,并且将找到的第一个多出来的右括号改成左括号 
											break;//改完就走 
										}
									} 
								}
								else
								{
									if(b1==2&&b2==2)
									{
										int kel=1;
										//几乎同上,只不过把寻找多出来的右括号换成寻找左括号,然后从j-2找到1
										 for(int l=j-2;l>=0;l--)
										{
											int seds=(bit>>(l*2))%4;
											if(seds==1)
												kel--;
											if(seds==2)
												kel++;
											if(kel==0)
											{
												INSERT(bit-inc[j-1]*2-inc[j]*2+inc[l],num);//将两个配对的括号消去,并且将找到的第一个多出来的左括号改成右括号 
												break;//改完就走 
											}
										} 
									}
									else
									{
										if(b1==2&&b2==1)
										{
											//直接连接起来就好
											INSERT(bit-inc[j-1]*2-inc[j],num); 
										}
										else
										{
											if(ex==i&&ey==j)
											{
												//这时就已经只剩下b1==1,b2==2了
												//也就是此时一定行成了一个闭环,如果不是终点那一定不对
												ans+=num; 
											}
										}
									} 
								}
							}
						}
					}
				} 
			}
		} 
	}
} 
signed main()
{
	freopen("formula1.in","r",stdin);
	freopen("formula1.out","w",stdout);
	init();
	WORK();
	printf("%lld",ans); 
	return 0;
}