题解:P11244 吻秋

· · 题解

诈骗题。

看着很复杂,赛事想了很多做法都不行,然后就去睡觉了。

睡醒比赛结束之后再想了想发现就是个诈骗题。

显然可以将所有数组都先排好序,下面假定所有数组都是单调不降的。

如果两个数组 a_x,a_y 直接合并起来就是单调不降的,我们称 a_x<a_y,a_y>a_x

如果两个数组 a_x,a_y 满足 a_x<a_y 或者 a_x>a_y,那么称这两个数组不相交;反之我们称这两个数组相交

我们设 c_x 为小于第 x 个数组的数组的数量。

对于操作一,操作完后必然满足 a_x<a_y

那么如果 a_x<a_y 那么操作就相当于作废;如果 a_x>a_y 那么操作相当于交换 a_x,a_y 同时也要交换 c_x,c_y

而对于其他情况,操作完之后 a_x<a_y,那么c_y1

发现了什么?

$c$ 数组的和最大为 $\frac{m(m-1)}{2}$,也就是任意两个数组都不相交的时候。 由于 $m\leq 20$,这启示我们当 $a_x,a_y$ 相交的时候暴力归并,这样最多处理 $O(m^2)$ 次;而剩余情况是 $a_x,a_y$ 不相交,这样的操作一是可以 $O(1)$ 完成的。 操作二查询是简单的,不多赘述。 最后时间复杂度 $O(mn\log n+m^2n+q)$。 感觉归并会快一点但为什么好像直接快排的 $O(mn+m^2n\log n+q)$ 还更快(大雾)。 代码: ```cpp #include<bits/stdc++.h> using namespace std; inline int read(){ int x(0),flg(1);char c(getchar()); while(c<'0'||c>'9'){if(c=='-') flg=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*flg; } void write(int x){ if(x>9) write(x/10); putchar(x%10^48); } int n,m,q; vector <int> a[25],b; int flg[25],id[25]; int main(){ n=read();m=read();q=read(); for(int i(1);i<=m;++i) id[i]=i; for(int i(1);i<=m;++i) for(int j(0);j<n;++j) a[i].push_back(read()); b.resize(n<<1); while(q--){ int op(read()),x(read()),y(read()); if(op==1){ if(!flg[id[x]]) sort(a[id[x]].begin(),a[id[x]].end()),flg[id[x]]=1; if(!flg[id[y]]) sort(a[id[y]].begin(),a[id[y]].end()),flg[id[y]]=1; if(a[id[x]][n-1]<=a[id[y]][0]) continue; if(a[id[y]][n-1]<=a[id[x]][0]){ swap(id[x],id[y]); continue; } x=id[x];y=id[y]; for(int i(0),j(0),k(0);i<n;++i){ while(j<n&&a[y][j]<a[x][i]) b[k++]=a[y][j++]; b[k++]=a[x][i]; if(i==n-1) while(j<n) b[k++]=a[y][j++]; } for(int i(0);i<n;++i) a[x][i]=b[i],a[y][i]=b[i+n]; } else write(a[id[x]][y-1]),puts(""); } return 0; } ```