CF1368G Shifting Dominoes

CF 1368 G

题目链接

点击打开

题目叙述

给出一个(n imes m)的棋盘,布满了 (1 imes2) 的多米诺骨牌,告诉你多米诺骨牌的排列情况。每次去掉一个股派,然后可以将其他骨牌挪动。每个骨牌都会被去掉一次。挪动后必须与最开始的位置至少公用一个位置。并且骨牌挪动方向必须平行于骨牌原本的方向(即不能“拐弯”)。问挪动若干次后剩下的两个空格的方案数。

题解

问题转化

考虑将骨牌移动想象成空格移动。对于一个空位置 ((r,c)) (第 (r) 行第 (c) 列),如果 ((r,c)) 下面 ((r+1,c),(r+2,c)) 是一个多米诺骨牌,那么 ((r,c)) 这个空格可以移动到 ((r+2,c)) 。这样连出若干条边,构成一个图。容易知道每个点的入读均为 1.

下面证明这是一个树。只要证明不存在一个环即可。如果存在一个环,那么一定形如这样:

考虑下面这样的形状:

可以将这样的东西向外翻,一次将会添加 4 个格子,这样不断操作一定能操作成一个长方形。然后可以发现中间空出来一个长宽均为奇数的长方形,很明显这样多米诺骨牌并不能将这个长方形填满。

解决

所以像上面那样连边将会连出一个森林。只要一对格子可以被两个组成一个多米诺骨牌的格子移动到,那么就算 1 的贡献。即对于两个结点,存在两个祖先满足他们组成多米诺骨牌的两个格子,那么就算 1 的贡献。

这个问题可以转化为一个矩阵面积并的问题。考虑将每一对点 ((x,y)) ,将其对应到平面上的点 ((dfn_x,dfn_y))(dfn_i)(i) 结点的 (dfs) 序)。现在枚举每一块骨牌,如果是由编号为 (x)(y) 的树上的结点所对应的方格组成,那么就将 (x) 的子树所对应的区间作为 (x) 轴的范围, (y) 的子树对应的区间最为 (y) 轴的范围,给这样组成的矩形中的所有点覆盖。通过一共覆盖的点数量即可算出答案。覆盖点数只需要使用扫描线求解即可。

实现

会发现树上结点 (x)(y) 组成的矩形与 (y)(x) 组成的矩形不一样。仔细思考可以发现都覆盖上,再将答案/2即可。

#include <cstdio>
#include <algorithm>
using namespace std;
const int NTOT = 2e5 + 5;
int N, M, deg[NTOT], root[NTOT], totr;
int in[NTOT], out[NTOT], dfstime;
char str[NTOT];
inline int dw(int r, int c) { return (r-1)*M+c; }
int head[NTOT];
struct E {
	int v, nxt;
	E(int _v, int _n) : v(_v), nxt(_n) {}
	E() {}
} edge[NTOT*2];
int totE;
void AddEdge(int u, int v) {
	edge[++totE] = E(v, head[u]);
	head[u] = totE;
}
void Dfs(int u) {
	in[u] = ++dfstime;
	for (int p = head[u]; p; p = edge[p].nxt) {
		int v = edge[p].v;
		Dfs(v);
	}
	out[u] = dfstime;
}
int tsqr;
struct Square {
	int u, d, l, r;
	Square(int _u, int _d, int _l, int _r) : u(_u), d(_d), l(_l), r(_r) {}
	Square() {}
} sqr[NTOT];
int tqz;
struct smx {
	int u, d, ps, ad;
	smx(int _u, int _d, int _p, int _a) : u(_u), d(_d), ps(_p), ad(_a) {}
	smx() {}
} qz[NTOT*2];
bool cmp(smx &a, smx &b) { return a.ps < b.ps; }
int tag[NTOT*4], cnt[NTOT*4];
int Len(int L, int R) { return R - L + 1; }
int Cal(int p, int L, int R) {
	if (!tag[p]) return cnt[p];
	else return Len(L, R);
}
void AddSeg(int p, int L, int R, int ql, int qr, int a) {
	if (qr < L || R < ql)
		return ;
	if (ql <= L && R <= qr) {
		tag[p] += a;
		return ;
	}
	int m = (L + R) >> 1;
	AddSeg(p<<1, L, m, ql, qr, a);
	AddSeg(p<<1|1, m+1, R, ql, qr, a);
	cnt[p] = Cal(p<<1, L, m) + Cal(p<<1|1, m+1, R);
}
int main() {
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= N; ++i)
		scanf("%s", str + (i-1) * M + 1);
	for (int i = 1; i <= N; ++i)
		for (int j = 1; j <= M; ++j) {
			if (i-2 >= 1 && str[dw(i-2, j)] == 'U') {
				AddEdge(dw(i, j), dw(i-2, j));
				++deg[dw(i-2, j)];
			} 
				
			if (j-2 >= 1 && str[dw(i, j-2)] == 'L') {
				AddEdge(dw(i, j), dw(i, j-2));
				++deg[dw(i, j-2)];
			}
				
			if (i+2 <= N && str[dw(i+2, j)] == 'D') {
				AddEdge(dw(i, j), dw(i+2, j));
				++deg[dw(i+2, j)];
			}
				
			if (j+2 <= M && str[dw(i, j+2)] == 'R') {
				AddEdge(dw(i, j), dw(i, j+2));
				++deg[dw(i, j+2)];
			}
		}
	for (int i = 1; i <= N*M; ++i)
		if (!deg[i])
			root[++totr] = i;
	for (int i = 1; i <= totr; ++i)
		Dfs(root[i]);
	for (int i = 1; i <= N; ++i)
		for (int j = 1; j <= M; ++j) {
			if (str[dw(i, j)] == 'U' && str[dw(i+1, j)] == 'D') {
				sqr[++tsqr] = Square(in[dw(i, j)], out[dw(i, j)], in[dw(i+1, j)], out[dw(i+1, j)]);
				sqr[++tsqr] = Square(in[dw(i+1, j)], out[dw(i+1, j)], in[dw(i, j)], out[dw(i, j)]);
			} else if (str[dw(i, j)] == 'L' && str[dw(i, j+1)] == 'R') {
				sqr[++tsqr] = Square(in[dw(i, j)], out[dw(i, j)], in[dw(i, j+1)], out[dw(i, j+1)]);
				sqr[++tsqr] = Square(in[dw(i, j+1)], out[dw(i, j+1)], in[dw(i, j)], out[dw(i, j)]);
			}
		}
	for (int i = 1; i <= tsqr; ++i) {
		qz[++tqz] = smx(sqr[i].u, sqr[i].d, sqr[i].l, 1);
		qz[++tqz] = smx(sqr[i].u, sqr[i].d, sqr[i].r+1, -1);
	}
	sort(qz + 1, qz + tqz + 1, cmp);
	long long ans = 0;
	for (int i = 1; i <= tqz; ++i) {
		AddSeg(1, 1, N*M+1, qz[i].u, qz[i].d, qz[i].ad);
		if (i != tqz) ans += (long long)Cal(1, 1, N) * (qz[i+1].ps - qz[i].ps);
//		for (int j = 1; j <= 8; ++j)
//			printf("tag[j] : %d cnt[j] : %d
", tag[j], cnt[j]);
	}
	printf("%lld
", ans / 2);
	return 0;
}

总结

  • 点对数量转化为平面上点的数量
  • 一个点是另一个点的子孙可以转化为 dfs 序的区间限制,可以在计数题上用到
原文地址:https://www.cnblogs.com/YouthRhythms/p/13788040.html