题目描述:
题目大意:
给一个n*m的矩阵,每个格子都有一个数字v,每次交换两个大小相同的不重叠的子矩阵,输出最后的矩阵。
输入格式:
第一行三个整数n,m,q代表表格的行数和列数和操作次数;
接下来n行,每行m个整数,表示格中的数字。
接下来q行,每行六个数字, r1,c1,r2,c2,h,w ,分别表示第一个矩形左上角所在行、所在列,第二个矩形左上角所在行、所在列,这两个矩形的高度和宽度(保证着两个矩形都在原有表格内)。
输出格式:
输出n行,每行m个整数,表示最后的矩阵。
样例输入:
4 4 2
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
1 1 3 3 2 2
3 1 1 3 2 2
样例输出:
4 4 3 3
4 4 3 3
2 2 1 1
2 2 1 1
数据范围:
对于10%的数据,所有的v=1;
对于30%的数据,n,m≤300,q≤300;
对于60%的数据,n,m≤1000,q≤500;
对于100%的数据,2≤n,m≤1000,1≤q≤500;1≤v≤1000000。
题目分析:
先吐槽一下这道题,因为题面中提到高级数据结构,然后有许多人写了splay,出题人为了卡那些人,后面的数据的询问就专门针对splay,结果导致暴力踩标算,直接拿满(暴力就是指直接模拟交换)。后来后面数据改了一下还是有80分,加register还是可以满分。(主要是数据很难兼顾同时卡暴力和splay)。
分析:这道题其实是一道链表题,我们可以记录四个方向(上下左右),每次交换,其实只用修改两个矩形的四周那一圈的连接。于是时间复杂度O(n*q)。
具体操作:先从起点(初始为(1,1),如果(1,1)与其它点交换,则这个点就成为了起点(因为实际上它被换到了(1,1)的位置))出发,分别走到这两个矩形的左上角,然后绕矩形走一圈(在两个矩形上的点是对应着一起走),每次交换两个矩形对应点连出去的指向和外面被连接的点所连回来的指向。这样就相当于交换整个矩形。
还可以优化:只记录两个指向,右、下,因为你输出只根据向右和向下就可以输出,而向右和向下就可以表示4个方向(上面的情况是在双向修改,这里就是单向的)。
链表可以一般写法,也可以指针写。
下附4份代码:1、暴力,2、我写的记录四个方向的链表,3、标算的记录两个方向的链表,4、记录两个方向的链表的指针写法。
附代码:
1、暴力+register:
是我考试代码直接加的register,所以还有特判部分
#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;
const int N=1e3+100;
int n,m,q,a[N][N];
bool check;
int readint()
{
char ch;int i=0,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') {ch=getchar();f=-1;}
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
return i*f;
}
int main()
{
freopen("table.in","r",stdin);
freopen("table.out","w",stdout);
n=readint();m=readint();q=readint();
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
{
a[i][j]=readint();
if(a[i][j]!=1) check=true;
}
if(check==false)
{
<