题目
题意:
给定2*n的数组,每次可以交换同一列的两个元素,要求使用尽可能少的操作使得每一维都形成一个n的排列。
1
≤
n
≤
200000
1≤n≤200000
1≤n≤200000
分析:
要想满足条件,那么1-n的元素都要出现两次。考虑对于一个x,如果两个x在同一行,那么一定只能交换一列。如果在不同行,那么这两列要么不交换,要么都交换。对于这种双向边的操作,就可以用并查集来维护,而有向边则需要用2-sat。
所以我们使用一个并查集,i代表第i列需要操作,i+n代表第i列不需要操作。对于上述两种情况分别合并集合。对于某一列i,若i与i+n在同一集合,则无解。由于要操作尽可能少,所以我们需要维护一个并查集的操作的次数。对于一列i,由于需要满足i或i+n,且只能一个,若所在集合的操作没有确定,那么我们选择一个操作少的集合满足i或i+n。
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 2e5 + 5;
int parent[2*maxn],flag[2*maxn],cost[2*maxn];
vector<int> res;
int a[maxn],b[maxn],cnt[maxn];
int find(int p)
{
if( p == parent[p] ) return p;
return parent[p] = find(parent[p]);
}
void unionx(int x,int y)
{
int fx = find(x);
int fy = find(y);
if( fx == fy ) return;
parent[fx] = fy;
cost[fy] += cost[fx];
}
int locate1[maxn],locate2[maxn];
void slove()
{
int n;
cin >> n;
int ans = 0;
res.clear();
for (int i = 1; i <= 2*n; i++)
{
if( i <= n )
{
locate1[i] = locate2[i] = 0;
cnt[i] = 0;
cost[i] = 1;
}else cost[i] = 0;
parent[i] = i;
flag[i] = 0;
}
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt[a[i]] ++;
if( locate1[a[i]] == 0 ) locate1[a[i]] = i;
else locate2[a[i]] = i;
}
for (int i = 1; i <= n; i++)
{
cin >> b[i];
cnt[b[i]] ++;
if( locate1[b[i]] == 0 ) locate1[b[i]] = i + n;
else locate2[b[i]] = i + n;
}
for (int i = 1; i <= n; i++)
{
if( cnt[i] != 2 )
{
cout << -1 << '\n';
return;
}
}
for (int i = 1; i <= n; i++)
{
int t1 = locate1[i],t2 = locate2[i];
if( t1 > n ) t1 -= n;
if( t2 > n ) t2 -= n;
if( (locate1[i] <= n && locate2[i] <= n) || (locate1[i] > n && locate2[i] > n) )
{
//cout << t1 << ' ' << t2 + n << '\n';
unionx(t1,t2+n);
unionx(t1+n,t2);
}else
{
//cout << t1 << ' ' << t2 << '\n';
unionx(t1,t2);
unionx(t1+n,t2+n);
}
}
for (int i = 1; i <= n; i++)
{
int fx = find(i);
int fy = find(i+n);
if( fx == fy )
{
cout << -1 << '\n';
return;
}
if( flag[fx] == 0 )
{
if( cost[fx] < cost[fy] )
{
flag[fy] = -1;
flag[fx] = 1;
}
else
{
flag[fy] = 1;
flag[fx] = -1;
}
}
if( flag[fx] > 0 ) res.push_back(i);
}
cout << res.size() << '\n';
for (int i = 0; i < res.size(); i++)
{
cout << res[i];
if( i != res.size() - 1 ) cout << ' ';
}
cout << '\n';
}
int main()
{
int t;
cin >> t;
while( t-- )
{
slove();
}
return 0;
}