ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【分类讨论 LIS】luogu_P7796 POLICE

2021-08-11 07:31:34  阅读:218  来源: 互联网

标签:sz 连通 POLICE 书架 int luogu 位置 P7796 1001


题意

给出一个矩阵与目标矩阵。
有两种操作:
1、移动数字到相邻(左右)空位置,耗费0。
2、拿出数字放到空位置,耗费1。
求出最小耗费完成转换。

对于\(50\%\)的数据,保证每本书在初始和最终状态下都会在同一个书架上。
对于所有数据,\(1\leqslant n,m\leqslant1000\)。

思路

对于\(50\%\),考虑每行怎么计算。
将每本书它的目标位置存到数组中。
对于这个数组求\(LIS\),那么答案即为\(sz_i-lis_i\),其中\(sz_i\)表示起始与目标在相同书架上的书的个数。
因为移到空位置不需耗费,对于一段前后顺序在操作前与操作后不变的书,可以通过空位置来调整。其余都要抽出。这个最长一段即\(LIS\)。

对于\(50\%\),考虑有书本要移到其他书架上的情况。
如果有书要从书架\(i \to j\),将其连边。
形成若干连通块。

对于一个连通块,若里面无空书架,要用其他的书架来进行中转,且目标状态也无空书架。
为了空出一个空书架,这个连通块里的操作次数加上\(1\)。

可以发现这个连通块形成一个欧拉回路,在空出一个位置后,其他书架按照欧拉回路来移动,都会出现空出一个位置的情况。
对于一个有空书架的书架里答案就为上面所说的\(sz_i-lis_i\)。

对于一个连通块,若里面一开始存在空书架,不用加上\(1\)即可。

连边用并查集来操作。

贴一个更详细分类讨论的博客

代码

#pragma GCC optimize(2)
#pragma GCC optimize("Ofast")
#include <queue>
#include <cstdio>
#include <cstring>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
std::queue<int> q;
int n, m, ans;
int a[1001][1001], emp[1001], sz[1001], lis[1001], fa[1001];
std::pair<int, int> b[1000001];
int tree[1001];

void add(int p, int val) {
	for (; p <= m; p += p & -p)
		tree[p] = std::max(val, tree[p]);
}

int ask(int p) {
	int res = 0;
	for (; p; p -= p & -p)
		res = std::max(res, tree[p]);
	return res;
}

int find(int x) {
	return fa[x] = fa[x] == x ? x : find(fa[x]);
}

void con(int x, int y) {
	int f1 = find(x), f2 = find(y);
	if (f1 == f2)
		return;
	fa[f2] = f1;
	emp[f1] |= emp[f2];
	sz[f1] += sz[f2];
	lis[f1] += lis[f2];
}

inline int read() {
	int res = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		res = res * 10 + (c ^ 48), c = getchar();
	return res * f;
}

int main() {
	freopen("librarian.in", "r", stdin);
	freopen("librarian.out", "w", stdout);
	n = read(), m = read();
	int dif = 0, empt = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			a[i][j] = read();
			if (!a[i][j])
				emp[i] = 1, empt = 1;
			else
				sz[i]++;
		}
	for (int i = 1, tmp; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			tmp = read();
			if (tmp)
				b[tmp] = std::make_pair(i, j);
			if (a[i][j] != tmp)
				dif = 1;
		}
	if (!dif)
		return !printf("0");
	if (dif && !empt)
		return !printf("-1");
	for (int i = 1; i <= n; i++) {
		memset(tree, 0, sizeof(tree));
		for (int j = 1; j <= m; j++)
			if (b[a[i][j]].first == i)
				q.push(b[a[i][j]].second);
		while (q.size()) {
			 int x = q.front(), tmp = ask(x);
			 q.pop();
			 lis[i] = std::max(lis[i], tmp + 1);
			 add(x, tmp + 1);
		}
	}
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j] && b[a[i][j]].first != i)
				con(i, b[a[i][j]].first);
	for (int i = 1; i <= n; i++)
		if (find(i) == i && sz[i] != lis[i])
			ans += sz[i] - lis[i] + (1 - emp[i]);
	printf("%d", ans);
}

标签:sz,连通,POLICE,书架,int,luogu,位置,P7796,1001
来源: https://www.cnblogs.com/HSZGB/p/15126034.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有