Obtain a Permutation
题目描述:
Input:
The first line of the input contains two integers nn and mm (1≤n,m≤2⋅105,n⋅m≤2⋅1051≤n,m≤2⋅105,n⋅m≤2⋅105) — the size of the matrix.
The next nn lines contain mm integers each. The number at the line ii and position jj is ai,jai,j (1≤ai,j≤2⋅1051≤ai,j≤2⋅105).
Output:
Print one integer — the minimum number of moves required to obtain the matrix, where a1,1=1,a1,2=2,…,a1,m=m,a2,1=m+1,a2,2=m+2,…,an,m=n⋅ma1,1=1,a1,2=2,…,a1,m=m,a2,1=m+1,a2,2=m+2,…,an,m=n⋅m (ai,j=(i−1)m+jai,j=(i−1)m+j).
Examples:
input1:
3 3
3 2 1
1 2 3
4 5 6
output1:
6
input2:
4 3
1 2 3
4 5 6
7 8 9
10 11 12
output2:
0
input3:
3 4
1 6 3 4
5 10 7 8
9 2 11 12
output:
2
思路:
先观察一下两种操作:
- 单点修改
- 整列上移
发现了什么?不同列之间互不影响,所以我们可以对于每一列分开处理。
那么,对于一列 jj,因为“先修改,后上移”和“先上移,后修改”没有本质区别,所以我们定义 same_{j, i}samej,i(0\le i <n0≤i<n)表示将第 jj 列上移 ii 个单位后,有几个数是不用变的。那么上移了 ii 个单位以后,自然还有 n - same_{j, i}n−samej,i 个数是需要变的,那么就要花费 n - same_{j,i}n−samej,i 次修改操作,上移也花费了 ii 次操作,那第 jj 列的最优方案就是 \min\limits_{0 \le i < n } \left(i + n - same_{j, i} \right)0≤i<nmin(i+n−samej,i),最终答案就是:
ans = \sum_{j = 1}^{m}\left( \min\limits_{0 \le i < n } \left(i + n - same_{j, i} \right) \right)ans=j=1∑m(0≤i<nmin(i+n−samej,i))
现在唯一的问题就是如何求出 samesame 了。我们发现,如果一个数 a_{i, j}ai,j 是 应该出现在第 \boldsymbol{j}j 列 的数字,那么一定有 j \le a_{i,j} \le n \times mj≤ai,j≤n×m,同时 a_{i, j} \equiv j \pmod{m}ai,j≡j(modm)(后者在代码里写作了 (a[i][j] - j) % m
)。
如果上面的条件满足了,那我们就可以算一下 a_{i,j}ai,j 这个数字应该出现在哪一行。显然,它应该出现在 \dfrac{a_{i,j} - j}{m} + 1mai,j−j+1 行(记作 kk)。
那如果我们把 a_{i,j}ai,j 移到第 kk 行,它就不用变了。回顾 samesame 的定义,从第 ii 行移到第 kk 行,要移动几次呢?
- 如果 i \ge ki≥k,移动 i - ki−k 个单位即可;
- 如果 i < ki<k,那首先把它移到第 11 行需要 i - 1i−1 次,然后再移 11 次到第 nn 行,从第 nn 行移到第 kk 行需要 n - kn−k 次,加起来,就是 i - 1 + 1 + n - ki−1+1+n−k,化简得 i - k + ni−k+n。
如果合并的话,就是要移动 (i - k + n) \bmod n(i−k+n)modn 次,那么我们就可以把 same_{j, (i - k + n) \bmod n}samej,(i−k+n)modn 增加 11 了。
注意 n, mn,m 都有可能达到 2 \times 10^52×105,所以需要开不定长数组,时间复杂度 \mathcal O(nm)O(nm)。samesame 可以每列重复使用,舍去前一个维度。
#include<bits/stdc++.h> #include<iostream> #include<cctype> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<stack> #include<map> using namespace std; typedef unsigned long long ull; typedef long long ll; typedef pair<ll,ll> pi; #define IOS std::ios::sync_with_stdio(false) #define ls p<<1 #define rs p<<1|1 #define mod 1000000000 + 7 #define PI acos(-1.0) #define INF 1e18 #define eps 1e-8 #define N 2000000 + 5 /*********************Code*********************/ ll n,m,s[N],ans; vector<ll>a[N]; int main(void){ IOS; cin>>n>>m; for(ll i = 1;i <=n;i++){ a[i].push_back(0); for(ll j = 1;j <=m;j++){ ll x; cin>>x; a[i].push_back(x); } } for(ll j = 1;j <=m;j++){ ll tans = INF; for(ll i = 0;i < n;i++) s[i] = 0; for(ll i = 1;i <=n;i++){ if(a[i][j]<j||a[i][j]>n*m||(a[i][j]-j)%m) continue; ll k = (a[i][j]-j)/m+1; s[(i-k+n)%n]++; } for(ll i = 0;i < n;i++) tans = min(tans,i+n-s[i]); ans +=tans; } cout<<ans<<endl; return 0; }