6493. 【GDOI2020模拟03.04】迷宫
题目描述
题解
da☆ze
人类智慧题
假如当前可能所在的集合为S,等价于以光速在每个可能的房间之间来回闪现
题目中的门是不同的,并且人物也能够看出具体是ABCD中的那扇门,但是不能通过门看到走到的房间情况,可以当成传送门
假设走入A门,根据所在具体房间的不同可能从ABCD四种门中走出,那么便可以判断出当前所在的集合
设S[i][j]表示当前从i门走入会走出门j的集合,f[s]表示可能集合为s的答案
那么当前的答案=min(max(f[S[A][A]],...,f[S[A][D]]),...,max(f[S[D][A]],...,f[S[D][D]]))+1
因为题目所求的是最坏条件,所以走进每个门都会走到最坏的集合中,答案即为最坏条件下最好的选择
发现每次只+1,所以可以直接bfs拓扑
因为答案只可能变大,如果一个max里面的四个集合都算出来了,那么便可以得到当前的答案
关于bfs能走到所有状态的证明:
设S中1的个数为层数,对于同一层必然会有一个靠近0点的状态,使得一个max内必然有一个状态在下一层
而又因为一种走法对应的四个状态无交,所以其余三个状态要么不存在要么也在下一层,因此一定可以从下一层推到上一层
会卡时间,把0点删掉变成2^(n-1)即可
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;
int a[8388609][3],ls[524288],p[21],b[21][4],B[21][4],c[524288][4],s[4][4],d[524288],f[524288],n,Q,i,j,k,l,x,y,len,L,h,t;
void New(int x,int y,int z)
{
++len;
a[len][0]=y;
a[len][1]=ls[x];
ls[x]=len;
a[len][2]=z;
++c[y][z];
}
char gc()
{
char ch;
ch=getchar();
while (ch<'A' || ch>'D')
ch=getchar();
return ch-'A';
}
int main()
{
freopen("maze.in","r",stdin);
#ifdef file
freopen("maze.out","w",stdout);
#endif
p[1]=1;
fo(i,2,20)
p[i]=p[i-1]*2;
scanf("%d%d",&n,&Q);L=p[n]-1;
fo(i,1,n+n)
{
scanf("%d",&x);k=gc();
scanf("%d",&y);l=gc();
b[x][k]=y;B[x][k]=l;
b[y][l]=x;B[y][l]=k;
}
// ---
fo(i,1,L)
{
memset(s,0,sizeof(s));
fo(j,1,n-1)
if (i&p[j])
{
fo(k,0,3)
s[k][B[j][k]]^=p[b[j][k]];
}
fo(j,0,3)
{
fo(k,0,3)
New(s[j][k],i,j);
}
}
// ---
h=0;t=1;
d[1]=0;f[0]=1;
while (h<t)
{
for (i=ls[d[++h]]; i; i=a[i][1])
if (!f[a[i][0]] && !(--c[a[i][0]][a[i][2]]))
{
f[a[i][0]]=f[d[h]]+1;
d[++t]=a[i][0];
}
}
// ---
for (;Q;--Q)
{
scanf("%d",&x);
printf("%d\n",f[x>>1]);
}
fclose(stdin);
fclose(stdout);
return 0;
}