题目
点这里看题目。
Burenka 有两张图片 $ a $ 和 $ b$,它们的大小可以表示为 $ n \times m $ 的像素组合。每幅画的每个像素都有一个颜色——表示为一个从 $0 $ 到 \(2 \times 10^5\) 的数字,并且在两幅画的每一行或每一列中都没有重复的颜色,除了颜色 $0 $。
Burenka 想把图片 \(a\) 变成图片 \(b\)。为了实现她的目标,Burenka 可以执行 \(2\) 种操作之一:交换 \(a\) 的任意两行或其任意两列。现在需要你来告诉 Burenka 该如何操作!
行从上到下被编号为 $ 1 $ 到 $ n $ ,列从左到右被编号为 $ 1 $ 到 $ m $。
所有数据满足 \(1\le n,m,n\times m\le 2\times 10^5\)。
分析
很特别的题目。
最重要的就是找到一个合适的方法描述这个问题。我们相当于要找到 \(p\in S_n,q\in S_m\),并对于行作用置换 \(p\)、对于列作用置换 \(q\),要求产生的矩阵 \(=b\)。
考虑网格的一种转化方式:行作为左部点,列作为右部点;若有非零元素 \(a_{i,j}\),则在左部 \(i\) 和右部 \(j\) 之间连一条颜色为 \(a_{i,j}\) 的边。记 \(a\) 产生的二分图为 \(G_a\),\(b\) 产生的二分图为 \(G_b\)。这样,存在需要的置换 \((p,q)\) 就等价于 \(G_a,G_b\) 作为二分图同构。
如何判定?不妨假设 \(n\le m\)。枚举 \(G_a\) 的左部点 \(x\) 和 \(G_b\) 的右部点 \(y\),检查它俩能不能匹配上。首先需要检查二者的出边的颜色集合是否相同;其次,因为一种颜色的出边至多只有一种,所以同颜色出边的终点一定会匹配在一起,接着就是递归检查。如果最终检查结果为 positive,则我们相当于找到了 \(x\) 所在连通块和 \(y\) 所在连通块的匹配方案,贪心地直接匹配上就好,否则就什么也干不了。这样一次检查的复杂度线性于 \(x\) 所在连通块的边数加 \(y\) 所在连通块的边数。
这样做复杂度还是不太友好。一个简单的剪枝是:如果 \(x\) 所在连通块和 \(y\) 所在连通块的左右二部的点数不同,那么一定匹配不上,无需检查。
这样再来分析复杂度——设 \(G_b\) 中连通块为 \((n_1,m_1),(n_2,m_2),\dots,(n_k,m_k)\)(\((n,m)\) 表示一个 \(n\) 个左部点、\(m\) 条边的连通块),则复杂度不超过 \(O(\sum_i n_i^2m_i\cdot \frac{n}{n_i})=O(n\sum_i n_im_i)=O(n^2m)\),在 \(n\le m\) 时就有 \(O(n^2m)=O((nm)^{1.5})\)。
代码
#include <bits/stdc++.h>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
const int MAXN = 2e5 + 5;
template<typename _T>
inline void Read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
inline void Write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) Write( x / 10 );
putchar( x % 10 + '0' );
}
typedef std :: pair<int, int> Edge;
typedef std :: pair<int, int> Candidate;
struct DSU {
int fa[MAXN << 1], siz1[MAXN << 1], siz2[MAXN << 1];
void MakeSet( const int &n, const int &m ) {
rep( i, 1, n ) fa[i] = i, siz1[i] = 1, siz2[i] = 0;
rep( i, n + 1, n + m ) fa[i] = i, siz1[i] = 0, siz2[i] = 1;
}
int FindSet( const int &u ) {
return fa[u] == u ? u : ( fa[u] = FindSet( fa[u] ) );
}
inline void UnionSet( int u, int v ) {
u = FindSet( u ), v = FindSet( v );
if( u == v ) return ;
fa[u] = v, siz1[v] += siz1[u], siz2[v] += siz2[u];
}
inline std :: pair<int, int> GetSize( int u ) {
u = FindSet( u );
return std :: make_pair( siz1[u], siz2[u] );
}
};
std :: tuple<int, int, int> ans[MAXN << 1];
int cnt = 0;
int seq[MAXN];
int matA[MAXN << 1], matB[MAXN << 1];
bool vis[MAXN << 1];
DSU dsuA, dsuB;
std :: vector<Edge> grphA[MAXN << 1], grphB[MAXN << 1];
int timA[MAXN << 1], timB[MAXN << 1], curTim;
Candidate candi[MAXN << 1]; int tot = 0;
int A[MAXN], B[MAXN];
int N, M;
#define GetId( x, y ) ( ( (x) - 1 ) * M + (y) )
inline bool Chk( const int &x, const int &y ) {
if( matA[x] || matB[y] )
return matA[x] == y && matB[y] == x;
if( grphA[x].size() != grphB[y].size() ) return false;
int n = grphA[x].size();
rep( i, 0, n - 1 )
if( grphA[x][i].first != grphB[y][i].first )
return false;
candi[++ tot] = { x, y };
matA[x] = y, matB[y] = x;
rep( i, 0, n - 1 )
if( ! Chk( grphA[x][i].second, grphB[y][i].second ) )
return false;
return true;
}
int main() {
// freopen( "impression.in", "r", stdin );
// freopen( "impression.out", "w", stdout );
bool swp = false;
Read( N ), Read( M );
if( N <= M ) {
rep( i, 1, N ) rep( j, 1, M )
Read( A[GetId( i, j )] );
rep( i, 1, N ) rep( j, 1, M )
Read( B[GetId( i, j )] );
} else {
swp = true;
std :: swap( N, M );
rep( i, 1, M ) rep( j, 1, N )
Read( A[GetId( j, i )] );
rep( i, 1, M ) rep( j, 1, N )
Read( B[GetId( j, i )] );
}
dsuA.MakeSet( N, M );
dsuB.MakeSet( N, M );
rep( i, 1, N ) rep( j, 1, M )
if( A[GetId( i, j )] ) {
int c = A[GetId( i, j )];
grphA[i].emplace_back( c, j + N );
grphA[j + N].emplace_back( c, i );
dsuA.UnionSet( i, j + N );
}
rep( i, 1, N ) rep( j, 1, M )
if( B[GetId( i, j )] ) {
int c = B[GetId( i, j )];
grphB[i].emplace_back( c, j + N );
grphB[j + N].emplace_back( c, i );
dsuB.UnionSet( i, j + N );
}
rep( i, 1, N + M ) std :: sort( grphA[i].begin(), grphA[i].end() );
rep( i, 1, N + M ) std :: sort( grphB[i].begin(), grphB[i].end() );
rep( i, 1, N ) {
if( matA[i] ) continue;
bool any = false;
rep( j, 1, N )
if( ! matB[j] && dsuA.GetSize( i ) == dsuB.GetSize( j ) ) {
tot = 0, curTim ++;
if( Chk( i, j ) ) {
any = true; break;
} else
rep( k, 1, tot )
matA[candi[k].first] = matB[candi[k].second] = 0;
}
if( ! any )
return puts( "-1" ), 0;
}
for( int i = N + 1, j = N + 1 ; i <= N + M ; i ++ ) {
if( matA[i] ) continue;
while( matB[j] ) j ++;
matA[i] = j, matB[j] = i;
}
for( int i = 1 ; i <= N ; i ++ ) {
seq[0] = 0;
for( int u = i ; ! vis[u] ; u = matB[u] )
seq[++ seq[0]] = u, vis[u] = true;
int opt = 1 + swp;
rep( j, 1, seq[0] - 1 )
ans[++ cnt] = std :: make_tuple( opt, seq[j], seq[j + 1] );
}
for( int i = 1 ; i <= M ; i ++ ) {
seq[0] = 0;
for( int u = i + N ; ! vis[u] ; u = matB[u] )
seq[++ seq[0]] = u - N, vis[u] = true;
int opt = 2 - swp;
rep( j, 1, seq[0] - 1 )
ans[++ cnt] = std :: make_tuple( opt, seq[j], seq[j + 1] );
}
Write( cnt ), putchar( '\n' );
rep( i, 1, cnt )
printf( "%d %d %d\n", std :: get<0>( ans[i] ),
std :: get<1>( ans[i] ),
std :: get<2>( ans[i] ) );
return 0;
}
标签:std,Impressionism,连通,le,Burenka,复杂度,times,CF1718E
From: https://www.cnblogs.com/crashed/p/17413592.html