题意
大致题意见题目描述,这里主要解释一下选手 x x x 「 l l l-强于」 选手 y y y:
以样例为例,对于第一个询问:
1 4
。在第一场比赛中选手 1 1 1 的排名已经比选手 4 4 4 的排名靠前,所以 l min = 1 l_{\min} = 1 lmin=1。对于第二个询问:
5 3
。虽然在第一场比赛中选手 5 5 5 比选手 3 3 3 差,但是比选手 4 4 4 好;而第二场比赛中选手 4 4 4 又比选手 3 3 3 好,所以 l min = 2 l_{\min}=2 lmin=2。对于第三个询问:
6 1
。我们用 x → y x \to y x→y 表示选手 x x x 比选手 y y y 在某一场比赛中排名更靠前,则有: 6 → 5 → 4 → 3 → 2 → 1 6\to5\to4\to3\to2\to1 6→5→4→3→2→1,所以 l min = 5 l_{\min}=5 lmin=5。对于第四个询问:
5 2
。那么有 5 → 4 → 3 → 2 5\to 4\to 3\to 2 5→4→3→2,所以 l min = 3 l_{\min}=3 lmin=3。
思路
暴力的做法是从小到大枚举步数,并维护不超过 k k k 步能到达的所有选手。注意到我们永远可以通过“ m m m 场比赛的前缀并”来描述下一步能到的所有选手,于是模拟过程中我们可以只维护 m m m 个选手编号。
考虑使用倍增加速,预处理每个跳 2 0 , 2 1 , 2 2 , … 2^0,2^1,2^2,\dots 20,21,22,… 步能到的 m m m 维最值即可,复杂度 O ( ( n + q ) m 2 log n ) O((n+q)m^2\log n) O((n+q)m2logn)。
具体地,我们记 f i , j , k f_{i,j,k} fi,j,k 表示 i i i 跳 2 j 2^j 2j 步,能到的第 k k k 场比赛考的最好的排名:
- j = 0 j=0 j=0 暴力预处理;
- j > 0 j>0 j>0 我们枚举中转点使用的比赛 c c c,然后先跳 f i , j − 1 , c f_{i,j-1,c} fi,j−1,c, f j , j , k ← f c , j − 1 , k f_{j,j,k}\leftarrow f_{c,j-1,k} fj,j,k←fc,j−1,k。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 10, maxk = 25;
int n,m,T,ans,a[maxm][maxn],b[maxm][maxn];
struct hyf { int c[maxm]; } f[maxk][maxn],g[maxm][maxn]; // 倍增数组
// 两个纯用于压行的函数,展开来写也彳亍
hyf Min(const hyf &x,const hyf &y) {
hyf res;
for (int i = 0;i < m;i ++) res.c[i] = min(x.c[i],y.c[i]);
return res;
}
bool operator >(const hyf &x,const hyf &y) {
for (int i = 0;i < m;i ++)
if (x.c[i] <= y.c[i]) return false;
return true;
}
hyf input(const int &x) {
hyf res;
for (int i = 0;i < m;i ++) res.c[i] = b[i][x];
return res;
}
int main() {
scanf("%d%d",&n,&m);
for (int i = 0;i < m;i ++)
for (int j = 1;j <= n;j ++){
scanf("%d",&a[i][j]);
b[i][a[i][j]] = j; // 第i场比赛中选手a[i][j]的名次为j
}
// 预处理倍增数组
// 处理 j = 0
for (int i = 0;i < m;i ++)
for (int j = n,x;j >= 1;j --) {
x = a[i][j], g[i][j] = input(a[i][j]);
if (j < n) g[i][j] = Min(g[i][j],g[i][j + 1]);
}
for (int i = 1;i <= n;i ++) {
f[0][i] = g[0][b[0][i]];
for (int j = 1;j < m;j ++)
f[0][i] = Min(f[0][i], g[j][b[j][i]]);
}
// 处理 j > 0
for (int b = 1;b <= 19;b ++)
for (int i = 1;i <= n;i ++) {
f[b][i] = f[b - 1][i];
for (int j = 0,k;j < m;j ++)
k = a[j][f[b - 1][i].c[j]],
f[b][i] = Min(f[b][i],f[b - 1][k]);
}
scanf("%d",&T);
for (int t = 1,x,y;t <= T;t ++) {
scanf("%d%d",&x,&y);
hyf st = input(x), en = input(y);
// 开始倍增跳
if (st > en) { // 需要跳几场比赛,也就是一开始st没有en厉害
int res = 0;
for (int i = 19;i >= 0;i --) {
hyf nxt = st;
for (int j = 0,k;j < m;j ++)
k = a[j][st.c[j]],
nxt = Min(nxt,f[i][k]);
if (nxt > en) res += 1 << i, st = nxt;
}
if (res > n) printf("-1\n");
else printf("%d\n",res + 2); // 记得加上开头 & 结尾
} else printf("1\n");
}
return 0;
}
- 评测记录:R127071409 记录详情