bzoj 1539 [POI2005]Dwu-Double-row 建图+思路

题面

题目传送门

解法

思路还是比较精妙的

  • 我们不妨假设 a [ i ] a[i] a[i]表示 i i i这一列是否交换两行的数
  • 然后对于一个数 x x x,假设它出现的位置分别为第 i i i列和第 j j j列,如果这两个位置在同一行,那么 i , j i,j i,j之间连接一条为 1 1 1的无向边,表示 a [ i ] ≠ a [ j ] a[i]≠a[j] a[i]̸=a[j],否则在 i , j i,j i,j之间连接一条为 0 0 0的无向边,表示 a [ i ] = a [ j ] a[i]=a[j] a[i]=a[j]
  • 然后这个图会被分成若干个连通块,对于每一个连通块,假设 a [ i ] = 0 a[i]=0 a[i]=0的有 x x x个, a [ i ] = 1 a[i]=1 a[i]=1的有 y y y个,那么这个连通块对答案的贡献为 m i n ( x , y ) min(x,y) min(x,y),这个应该比较显然
  • 时间复杂度: O ( n ) O(n) O(n)

代码

#include <bits/stdc++.h>
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {int next, num, v;} e[N * 4];
int s0, s1, cnt, vis[N];
vector <int> pos[N];
void add(int x, int y, int v) {
	e[++cnt] = (Edge) {e[x].next, y, v};
	e[x].next = cnt;
}
void dfs(int x, int col) {
	if (col == 0) s0++; else s1++; vis[x] = 1;
	for (int p = e[x].next; p; p = e[p].next) {
		int k = e[p].num, v = e[p].v;
		if (vis[k]) continue; dfs(k, col ^ v);
	}
}
int main() {
	int n, mx = 0; read(n); cnt = n;
	for (int i = 1; i <= n; i++) {
		int x; read(x); chkmax(mx, x);
		pos[x].push_back(i);
	}
	for (int i = 1; i <= n; i++) {
		int x; read(x); chkmax(mx, x);
		pos[x].push_back(-i);
	}
	for (int i = 1; i <= mx; i++) {
		if (pos[i].size() < 2) continue;
		int x = pos[i][0], y = pos[i][1], tx = abs(x), ty = abs(y);
		if (x < 0 && y > 0 || x > 0 && y < 0) add(ty, tx, 0), add(tx, ty, 0);
			else add(ty, tx, 1), add(tx, ty, 1);
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		if (!vis[i]) {
			s0 = 0, s1 = 0;
			dfs(i, 0); ans += min(s0, s1);
		}
	cout << ans << "\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值