Luogu P4082 [USACO17DEC]Push a Box 点双连通分量/圆方树

(貌似有圆方树的做法,我写的是点双)

显然这道题就是直接搜索。定义状态为f[i][j][0~4]表示箱子在(i,j),人在某个方向推。然后问题就是怎么转向。我们发现如果要转向,必须是人走过一条不包括(i,j)的路径到另一个方向。那么直接求一个点双联通分量就完事了。把点双联通里面的点两两之间都有至少两条不经过重复点的路径,这样就能转向了。

点双内,具体的处理方法是开一个桶。

具体见代码

CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1505;
const int MAXP = MAXN*MAXN;
int n, m, q, id[MAXN][MAXN], tot, tmr;
int fir[MAXP], to[MAXP<<2], nxt[MAXP<<2], cnt, A, B;
int dfn[MAXP], low[MAXP], stk[MAXP], indx, scc[MAXP];
char s[MAXN][MAXN];
pair<int,int>pos[MAXP];

inline void link(int u, int v) {
	to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt;
	to[++cnt] = u; nxt[cnt] = fir[v]; fir[v] = cnt;
}
bool vis[MAXN][MAXN];
bool can[MAXN][MAXN][4][4];
//0 : L
//1 : R
//2 : U
//3 : D
const int Dx[4] = { 0, 0, -1, 1 };
const int Dy[4] = { -1, 1, 0, 0 };
inline void ins(int k) {
	vis[pos[k].first][pos[k].second] = 1;
	for(int u, v, i = 0; i < 4; ++i) {
		u = pos[k].first - Dx[i];
		v = pos[k].second - Dy[i];
		if(u >= 1 && u <= n && v >= 1 && v <= m && s[u][v] != '#') {
			for(int x, y, j = 0; j < 4; ++j) if(j!=i) { //可能x,y被重复枚举到,但是没关系,常数问题
				x = u + Dx[j], y = v + Dy[j];
				if(x >= 1 && x <= n && y >= 1 && y <= m && s[x][y] != '#' && vis[x][y])
					can[u][v][i][j] = can[u][v][j][i] = 1;
			}
		}
	}
}
inline void del(int k) {
	vis[pos[k].first][pos[k].second] = 0;
}
inline void tarjan(int u, int ff) {
	dfn[u] = low[u] = ++tmr;
	stk[++indx] = u;
	for(int v, i = fir[u], j; i; i = nxt[i])
		if((v=to[i]) != ff) {
			if(!dfn[v]) {
				tarjan(v, u), low[u] = min(low[u], low[v]);
				if(low[v] >= dfn[u]) {
					for(j = indx; ; --j) {ins(stk[j]); if(stk[j] == v) break; } ins(u);
					for(j = indx; ; --j) {del(stk[j]); if(stk[j] == v) break; } del(u);
					indx = j-1;
				}
			}
			else low[u] = min(low[u], dfn[v]);
		}
}

bool dis[MAXN][MAXN];
inline void bfs(int Sx, int Sy) {
	queue<pair<int,int> >Q;
	Q.push(pair<int, int>(Sx, Sy));
	dis[Sx][Sy] = 1;
	while(!Q.empty()) {
		int u = Q.front().first, v = Q.front().second; Q.pop();
		for(int x, y, i = 0; i < 4; ++i) {
			x = u + Dx[i], y = v + Dy[i];
			if(x >= 1 && x <= n && y >= 1 && y <= m && s[x][y] == '.' && !dis[x][y]) {
				dis[x][y] = 1, Q.push(pair<int, int>(x, y));
			}
		}
	}
}
struct node {
	int x, y, z;
	node(){}
	node(int x, int y, int z):x(x), y(y), z(z){}
};
bool f[MAXN][MAXN][4];
queue<node>que;
int main () {
	freopen("pushbox.in", "r", stdin);
	freopen("pushbox.out", "w", stdout);
	scanf("%d%d%d", &n, &m, &q);
	for(int i = 1; i <= n; ++i) {
		scanf("%s", s[i]+1);
		for(int j = 1; j <= m; ++j) {
			if(s[i][j] != '#') {
				id[i][j] = ++tot;
				pos[tot] = pair<int,int>(i, j);
				if(j > 1 && s[i][j-1] != '#') link(id[i][j-1], id[i][j]);
				if(i > 1 && s[i-1][j] != '#') link(id[i-1][j], id[i][j]);
			}
			if(s[i][j] == 'A') A = id[i][j];
			if(s[i][j] == 'B') B = id[i][j];
		}
	}
	tarjan(B, 0);
	bfs(pos[A].first, pos[A].second);
	int Sx = pos[B].first, Sy = pos[B].second;
	if(dis[Sx][Sy-1]) f[Sx][Sy][0] = 1, que.push(node(Sx, Sy, 0));
	if(dis[Sx][Sy+1]) f[Sx][Sy][1] = 1, que.push(node(Sx, Sy, 1));
	if(dis[Sx-1][Sy]) f[Sx][Sy][2] = 1, que.push(node(Sx, Sy, 2));
	if(dis[Sx+1][Sy]) f[Sx][Sy][3] = 1, que.push(node(Sx, Sy, 3));
	while(!que.empty()) {
		int u = que.front().x, v = que.front().y, w = que.front().z; que.pop();
		int x = u - Dx[w], y = v - Dy[w];
		if(x >= 1 && x <= n && y >= 1 && y <= m && s[x][y] != '#' && !f[x][y][w])
			f[x][y][w] = 1, que.push(node(x, y, w));
		for(int i = 0; i < 4; ++i)
			if(i != w && can[u][v][w][i] && !f[u][v][i]) {
				f[u][v][i] = 1, que.push(node(u, v, i));
			}
	}
	int Tx, Ty;
	while(q--) {
		scanf("%d%d", &Tx, &Ty);
		puts((s[Tx][Ty] == 'B' || f[Tx][Ty][0] || f[Tx][Ty][1] || f[Tx][Ty][2] || f[Tx][Ty][3]) ? "YES" : "NO");
	}
}

md变量写错调我2h。

posted @ 2019-12-14 14:50  _Ark  阅读(85)  评论(0编辑  收藏  举报