ICode9

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

【luogu P7796】图书管理员 / POLICE(并查集)(树状数组)

2021-08-24 09:34:33  阅读:240  来源: 互联网

标签:node POLICE 书架 int luogu 查集 re include 1001


图书管理员 / POLICE

题目链接:luogu P7796

题目大意

给你 n 个书架,每个书架有 m 个位置,然后有一些书在上面。
给出图书的初始放置状态和期望放置状态。
然后你有两个操作:
如果一本书左边或右边是空的,那你可以把这本书移到左边或右边。
从书架上拿走一本书,放在一个空位上。
然后问你整理到期望放置状态最少要用多少次拿书的操作,如果无法达到期望状态也要判断出来。

思路

首先考虑无解的情况,不难想到当且仅当没有空格子而且初始状态跟期望的不一样。

然后先看看部分分,不难想到把数重新按第一个的顺序重新编号,然后其实就是对第二个跑一个 LIS。
得出的 LIS 的值就可以通过第一个操作得到,剩下的就要移动了。然后不难想到如果这一列有空的,那就要移多少个就是多少,那如果没有空的,就要把一个移出去,移完剩下的再移回来,所以要比移的个数加一。

那你考虑这个移动会飞到另一个书架。
那如果完全解决的,就不用管了。
那你可以考虑建一个图,从 \(i\) 书架飞到 \(j\) 书架就 \(i\) 连向 \(j\)。

那你会发现你可以相当于建无向图的话,一个连通块之间才会相互关联。
那如果这连通块间没有空的,那每个点入度等于初度,就是一个欧拉回路,那你总要有一个拿出去,才可以把图跑掉,然后就可以放回去,所以这个是要加一的。
那如果有空的,那我们像一个书架里面的一样,类比一下(前面也可以类比),就是不用加。(具体就是你可以加几条虚的使它变成欧拉回路,那因为有虚的,我们从虚的出发开始跑欧拉回路,虚的就代表不操作,就不需要加一了)
不难看出这里不用真的建无向图,只需要用个并查集就可以了。

然后就好了。
具体的可以看看代码。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

struct node {
	int x, num, xx;
}c[1001];
int n, m, a[1001][1001], b[1001][1001];
int dy[1000001], tree[1001], ans, f[1001];
int lis[1001], pp[1001], tot[1001], fa[1001];
bool kong;

bool cmp1(node x, node y) {
	return x.x < y.x;
}

bool cmp2(node x, node y) {
	return x.num < y.num;
}

int query(int x) {
	int re = 0;
	for (; x; x -= x & (-x))
		re = max(re, tree[x]);
	return re;
}

void insert(int x, int y, int n) {
	for (; x <= n; x += x & (-x))
		tree[x] = max(tree[x], y);
}

int work(int x) {//求 LIS(进而求出一个书架单独的答案)
	int nn = 0, tmp = 0, re = 0;
	for (int i = 1; i <= m; i++)
		if (a[x][i]) dy[a[x][i]] = ++nn;
	for (int i = 1; i <= m; i++)
		if (b[x][i] && dy[b[x][i]]) c[++tmp].x = dy[b[x][i]], c[tmp].num = tmp;
	
	sort(c + 1, c + tmp + 1, cmp1);//离散化一下方便树状数组(重新编号)
	c[1].xx = 1;
	for (int i = 2; i <= tmp; i++)
		if (c[i].x == c[i - 1].x) c[i].xx = c[i - 1].xx;
			else c[i].xx = c[i - 1].xx + 1;
	sort(c + 1, c + tmp + 1, cmp2);
	
	memset(tree, 0, sizeof(tree));
	for (int i = 1; i <= tmp; i++) {
		f[i] = query(c[i].xx - 1) + 1;
		re = max(re, f[i]);
		insert(c[i].xx, f[i], nn);
	}
	
	lis[x] = re;
	pp[x] = nn;
	tot[x] = m;
	
	for (int i = 1; i <= m; i++)
		if (a[x][i]) dy[a[x][i]] = 0;
	
	return nn - re;
}

int find(int now) {
	if (fa[now] == now) return now;
	return fa[now] = find(fa[now]);
}

void connect(int x, int y) {
	int X = find(x), Y = find(y);
	if (X == Y) return ;
	lis[Y] += lis[X];
	pp[Y] += pp[X];
	tot[Y] += tot[X];
	fa[X] = Y;
}

int main() {
//	freopen("librarian.in", "r", stdin);
//	freopen("librarian.out", "w", stdout);
	
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			scanf("%d", &a[i][j]);
			if (!a[i][j]) kong = 1;
		}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) {
			scanf("%d", &b[i][j]);
			if (!kong && a[i][j] != b[i][j]) {//没有
				printf("-1");
				return 0;
			}
		}
	if (!kong) {//已经一模一样
		printf("0");
		return 0;
	}
	
	for (int i = 1; i <= n; i++) {
		ans += work(i);
	}
	
	for (int i = 1; i <= n; i++) fa[i] = i;
	memset(dy, 0, sizeof(dy));
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (b[i][j]) dy[b[i][j]] = i;//连边
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			if (a[i][j]) connect(i, dy[a[i][j]]);
	
	for (int i = 1; i <= n; i++)
		if (find(i) == i && lis[i] != pp[i])
			ans += (pp[i] == tot[i]);//如果这个块有空的就不用多挪一次,否则就要
	
	printf("%d", ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
} 

标签:node,POLICE,书架,int,luogu,查集,re,include,1001
来源: https://www.cnblogs.com/Sakura-TJH/p/luogu_P7796.html

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

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

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

ICode9版权所有