D. Fixed Prefix Permutations
题意
给定 n n n 个长度为 m m m 的排列 a 1 , a 2 , . . . a n a_1,a_2,...a_n a1,a2,...an
定义一个排列 p p p 的 价值 为 最大顺序长度 k k k: p 1 = 1 , p 2 = 2 , p 3 = 3 , . . . p k = k p_1 = 1,p_2 = 2, p_3 = 3, ... p_k = k p1=1,p2=2,p3=3,...pk=k,如果 p 1 ≠ 1 p_1 \neq 1 p1=1,那么 p p p 的价值为 0 0 0
定义两个排列的 乘积 为: p ⋅ q = r ,其中 r j = q p j p \cdot q = r,其中 r_j = q_{p_j} p⋅q=r,其中rj=qpj
现在对于每个 i ∈ [ 1 , n ] i \in [1, n] i∈[1,n],求出 最大 的 a i ⋅ a j a_i \cdot a_j ai⋅aj(注意 j j j 可以等于 i i i)
思路
对于当前的
a
i
a_i
ai,我们需要找到一个
j
j
j,使得
a
i
⋅
a
j
=
(
1
,
2
,
3
,
4
,
.
.
.
.
k
,
r
k
+
1
,
.
.
.
.
r
m
)
a_i \cdot a_j = (1,2,3,4,....k, r_{k + 1},....r_m)
ai⋅aj=(1,2,3,4,....k,rk+1,....rm) 中的
k
k
k 最大
如果
k
k
k 刚好等于
m
m
m 的话,
a
i
⋅
a
j
=
(
1
,
2
,
3
,
4
,
.
.
.
.
,
m
)
a_i \cdot a_j = (1, 2, 3, 4,...., m)
ai⋅aj=(1,2,3,4,....,m)
此时我们转换一下: a i = ( 1 , 2 , 3 , 4 , . . . , m ) ⋅ a j − 1 a_i = (1,2,3,4,..., m) \cdot a_j ^ {-1} ai=(1,2,3,4,...,m)⋅aj−1
不难发现,对于一个顺序度为 m m m 的排列 ( 1 , 2 , 3 , 4 , . . . , m ) (1,2,3,4, ..., m) (1,2,3,4,...,m),它乘上 a j − 1 a_j ^ {-1} aj−1 不会改变 a j − 1 a_j ^ {-1} aj−1 的内容
也就是:
a
i
=
a
j
−
1
a_i = a_j ^ {-1}
ai=aj−1;
更一般地,当
k
<
m
k < m
k<m 时,
a
i
a_i
ai 的前
k
k
k 位 与
a
j
−
1
a_j ^ {-1}
aj−1 的前
k
k
k 位是 一样 的!
那么我们只需要求出每个 a i a_i ai 的 逆,然后再与当前的 a i a_i ai 比较,最长公共前缀就是当前 a i a_i ai 的答案
如何求逆?由 p ⋅ p − 1 = [ 1 , 2 , 3 , 4 , . . . , m ] p \cdot p ^ {-1} = [1,2,3,4,..., m] p⋅p−1=[1,2,3,4,...,m] 得: p p j − 1 = j p ^ {-1}_{p_j} = j ppj−1=j,也就是以 p p p 为下标映射, p j p_j pj 指向的位置一定得为 j j j
那么我们只需要将每个逆存在 T r i e Trie Trie 上,对于当前的 a i a_i ai 在 T r i e Trie Trie 上跑 L C P LCP LCP (最大公共前缀)即可
时间复杂度: O ( n m ) O(nm) O(nm)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
const int N = 500050;
struct node{
int son[11];
}tree[N];
int cnt = 0;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin >> t;
while(t--){
int n, m;
std::cin >> n >> m;
std::vector<int> ans(n + 1, 0);
std::vector<std::vector<int>> a(n + 1, std::vector<int>(m + 1, 0));
fore(i, 1, n + 1)
fore(j, 1, m + 1)
std::cin >> a[i][j];
fore(i, 1, n + 1){
std::vector<int> b(m + 1);
fore(j, 1, m + 1) b[a[i][j]] = j;
int now = 0;
fore(j, 1, m + 1){ //Trie 插入 排列的逆
int val = b[j];
if(!tree[now].son[val]) tree[now].son[val] = ++cnt;
now = tree[now].son[val];
}
}
fore(i, 1, n + 1){
int now = 0;
fore(j, 1, m + 1){ //Trie 查询最长前缀
int val = a[i][j];
if(!tree[now].son[val]) break;
++ans[i];
now = tree[now].son[val];
}
}
fore(i, 1, n + 1) std::cout << ans[i] << " \n"[i == n];
fore(i, 0, cnt + 1) //清空 Trie
fore(j, 1, m + 1)
tree[i].son[j] = 0;
cnt = 0;
}
return 0;
}