P11244 解题报告
Brilliant11001 · · 题解
更好的阅读体验
题目传送门
题目大意已经很清楚,不再赘述。
思路:
将所有序列的状态分为
先给出一个定义,序列的跨度:
定义一个序列
第一阶段:
首先注意到
第二阶段:
当所有序列都有序后再进行操作
第三阶段:
这时候操作
那么为什么第二阶段的归并次数不会超过
这里我用了数学归纳法来证明。
命题:有 m 个序列且都有序的情况下,第二阶段的归并次数不会超过 \frac{m(m - 1)}{2} 。
当
当
那么当
引理1: 设两个序列为
x, y 且x,y 的跨度不是包含关系,y 的跨度的右端点大于等于x 的跨度右端点,则对于操作\texttt{1 x y} 进行了有效归并操作后,设原来的跨度分别为S_1,S_2 ,新得到的跨度分别为S_1',S_2' ,则有S_1'\subseteq S_1,S_2'\subseteq S_2 证明:
设
x 中的最大值为mx_x ,最小值是mn_x ,y 中的最大值为mx_y ,最小值是mn_y ,那么x 的跨度为S_1 = [mn_x, mx_x] ,y 的跨度为S_2 = [mn_y, mx_y] 。因为只有当
mx_x > mn_y 时才归并,且归并必定将足够多的等于mx_x 的项移到y 中,也必定会将足够多的等于mn_y 的项移到x 中。设新得到的
x,y 的跨度分别为S_1' = [l_x, r_x],S_2' = [l_y, r_y] ,那么必然有r_x \le mx_x,l_y \ge mn_y ,而l_x = mn_x,r_y = mx_y 这两个不会变,故有S_1'\subseteq S_1,S_2'\subseteq S_2 。证明完毕。
这说明:当所有操作的两个序列跨度不相互包含时,设势能函数
并且根据引理
但是还有一种可能:当两个序列的跨度相互包含的情况呢?
显然上述引理就不满足了,比如归并
表面上看起来第二个序列变为了
先考虑以下这种情况:
第一行表示的是已经互不相交的
左红圈表示
那么很轻松就能得出蓝色线一定是在
当蓝线往左移动时,
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2000010;
int n, m, q;
int a[21][N];
bool st[N];
int tmp[N << 1];
int id[21];
int main() {
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &a[i][j]);
for(int i = 1; i <= m; i++) id[i] = i;
int op, x, y;
while(q--) {
scanf("%d%d%d", &op, &x, &y);
if(op == 1) {
int xx = x, yy = y;
x = id[x], y = id[y];
if(st[x] && st[y]) {
if(a[x][n] > a[y][1]) {
if(a[x][1] >= a[y][n])
swap(id[xx], id[yy]);
else {
int pos1 = lower_bound(a[x] + 1, a[x] + n + 1, a[y][1]) - a[x];
int pos2 = lower_bound(a[y] + 1, a[y] + n + 1, a[x][n]) - a[y];
if(a[x][pos1] == a[y][1]) pos1++;
if(a[y][pos2] == a[x][n]) pos2--;
if(pos2 == n + 1) pos2--;
int p1 = pos1, p2 = 1, top = 0;
while(p1 <= n && p2 <= pos2) {
if(a[x][p1] < a[y][p2]) tmp[++top] = a[x][p1++];
else tmp[++top] = a[y][p2++];
}
while(p1 <= n) tmp[++top] = a[x][p1++];
while(p2 <= pos2) tmp[++top] = a[y][p2++];
top = 1;
for(int i = pos1; i <= n; i++)
a[x][i] = tmp[top++];
for(int i = 1; i <= pos2; i++)
a[y][i] = tmp[top++];
}
}
}
else {
if(!st[x]) sort(a[x] + 1, a[x] + n + 1);
if(!st[y]) sort(a[y] + 1, a[y] + n + 1);
st[x] = st[y] = true;
int pos1 = lower_bound(a[x] + 1, a[x] + n + 1, a[y][1]) - a[x];
int pos2 = lower_bound(a[y] + 1, a[y] + n + 1, a[x][n]) - a[y];
if(a[x][pos1] == a[y][1]) pos1++;
if(a[y][pos2] == a[x][n]) pos2--;
if(pos2 == n + 1) pos2--;
int p1 = pos1, p2 = 1, top = 0;
while(p1 <= n && p2 <= pos2) {
if(a[x][p1] < a[y][p2]) tmp[++top] = a[x][p1++];
else tmp[++top] = a[y][p2++];
}
while(p1 <= n) tmp[++top] = a[x][p1++];
while(p2 <= pos2) tmp[++top] = a[y][p2++];
top = 1;
for(int i = pos1; i <= n; i++)
a[x][i] = tmp[top++];
for(int i = 1; i <= pos2; i++)
a[y][i] = tmp[top++];
}
}
else printf("%d\n", a[id[x]][y]);
}
return 0;
}
其实也可以用 vector 直接
这一场怎么这么奇怪?