//法1:先模拟操作,算出最后的电子表格,接着在每次查询时直接在电子表格中,找到所求的单元格
/*
此方法最关键的地方,应是怎样建立原来的位置和后来的位置的联系
入门经典的法一,很巧妙地借助了这样的数组定义;
d[i][j] = i*BIG + j,记录下了原来的位置...在后面的插入、删除和交换等等步骤之后
虽然d[i][j]里装的数字可能变化了,可是只要计算 d[i][j] / BIG, d[i][j] % BIG,得到的就是这个位置的初始位置的行数和列数了
以及,填充ans数组时,相当于遍历每个位置,每个位置(i,j)会有一个对应的数字 i * BIG +j,我们可以找到它原来的行列位置,作为ans[i][j]中的i,j(怎么找呢,当然是用d数组里的值整除BIG和取模BIG啦~),因此有 ans[d[i][j] / BIG][d[i][j] % BIG] = i * BIG + j;
它的含义是:原来第i行第j列的数字,现在放到了新的位置,而我如果想知道这个新的位置,只要将 (i * BIG + j) 整除BIG和取模BIG即可
所以,如果填充ans数组时,遇到了原来存在但现在被删掉了的数字,那么原来的行数和列数,一定不会作为ans[i][j]中的i和j,因为既然删除,最初的那个d[i][j]肯定也就被删掉了
如果填充ans数组时,遇到了新加上的数,那么它之前肯定没被填充到d数组,而全局数组d,没被填充的部分就是0,所以,它根本不会影响ans数组的正常填充
总之,这题很巧妙地借助了取模和整除,建立了原来的位置和现在的位置之间的关系,以及表示位置的 i*BIG + j,入门经典中指定的BIG是10000,但其实100就够了,它的目的只是让i被分离出来时,一定不会受到j进位的影响,既然i,j都是最大50,也就是最多2位,BIG用100也就够了
BTW,这题和 HDU - 6098 Inversion 其实有些异曲同工之妙,虽然看上去很不同,但它们都有一个共同点,都要和最初的标号(或是位置)建立上联系,对这题的这种方法,是利用乘法+加法、除法+取模,来将原来的位置和现在的位置建立联系(它们之间有个不会变化的纽带,就是构造出的那个数字,d[i][j] / BIG, d[i][j] % BIG,于是这就保证了,对于某个位置而言,只要它没被删除,无论它移动到那里,在别的位置上,仍然可以通过构造出的那个数字,整除和取模得到原先的位置)
这种思路实在是精妙,让人想对刘汝佳前辈伏地膜...
而hdu那题,是通过构造结构体,将序号和数值绑定起来
(没记错的话,这道题我已经在我的blog上发了题解啦,需要的不妨看看)
两题的共性就是:都要用一些特定方法或思路,来保证一个不变量,作为最初和最终状态的纽带
*/
#include <iostream>
#include <cstring>
using namespace std;
const int maxd = 100;
const int BIG = 100;
int r, c, n, d[maxd][maxd], d2[maxd][maxd], ans[maxd][maxd], cols[maxd];
/*依次表示:行、列、操作次数
d:将d中的值按照最初位置构造 i * BIG + j并赋值,d中的值会改变位置,但通过值仍能推出初始位置(除非删除)
d2:起临时变量作用的数组,方便进行系列操作
ans:下标对应初始位置,值对应最终位置的构造值 i * BIG + j,整除取模可得最终位置
cols:表示每次插入或删除操作波及的行数或列数,因而每次操作前需清空
*/
void copy(char type, int p, int q)
{
if (type == 'R')
{
for (int i = 1; i <= c; i++)
d[p][i] = d2[q][i];
}
else
{
for (int i = 1; i <= r; i++)
d[i][p] = d2[i][q];
}
}
void del(char type)
{
memcpy(d2, d, sizeof(d));
int cnt = type == 'R' ? r : c, cnt2 = 0;
for (int i = 1; i <= cnt; i++)
{
if (!cols[i]) copy(type, ++cnt2, i);
}
if (type == 'R') r = cnt2; else c = cnt2;
}
void ins(char type)
{
memcpy(d2, d, sizeof(d));
int cnt = type == 'R' ? r : c, cnt2 = 0;
for (int i = 1; i <= cnt; i++)
{
if (cols[i]) copy(type, ++cnt2, 0);
copy(type, ++cnt2, i);
}
if (type == 'R') r = cnt2; else c = cnt2;
}
int main()
{
int r1, c1, r2, c2, q, kase = 0;
char cmd[10];
memset(d, 0, sizeof(d));
while (cin >> r >> c >> n && r)
{
int r0 = r, c0 = c;
for (int i = 1; i <= r; i++)
for (int j = 1; j <= c; j++)
d[i][j] = i * BIG + j;
while (n--)
{
cin >> cmd;
if (cmd[0] == 'E')
{
cin >> r1 >> c1 >> r2 >> c2;
swap(d[r1][c1], d[r2][c2]);
}
else
{
int a, x;
cin >> a;
memset(cols, 0, sizeof(cols));
for (int i = 0; i < a; i++)
{
cin >> x;
cols[x] = 1;
}
if (cmd[0] == 'D') del(cmd[1]);
else ins(cmd[1]);
}
}
memset(ans, 0, sizeof(ans));
for (int i = 1; i <= r; i++)
for (int j = 1; j <= c; j++)
{
ans[d[i][j] / BIG][d[i][j] % BIG] = i * BIG + j;
}
if (kase) cout << endl;
cout << "Spreadsheet #" << ++kase << endl;
cin >> q;
while (q--)
{
cin >> r1 >> c1;
cout << "Cell data in (" << r1 << "," << c1 << ") ";
if (ans[r1][c1] == 0) cout << "GONE" << endl;
else cout << "moved to (" << ans[r1][c1] / BIG << "," << ans[r1][c1] % BIG << ")" << endl;
}
}
return 0;
}
//法二:先将所有的操作保存,然后对于每个查询重新执行每个操作,但不需要计算整个电子表格的变化,而只关注所查询的单元格的位置变化。对于题目给定的规模来说,这个方法不仅更好写,而且效率更高
//入门经典的法二,代码就比较容易理解了,逻辑十分清楚
#include <iostream>
#include <cstring>
using namespace std;
typedef pair<int, int> p;
const int maxd = 10000;
struct Command
{
char c[5];
p pos1, pos2; //position 1, position 2
int a, x[20];
}cmd[maxd];
int r, c, n;
int simulate(int &r0, int &c0)
{
for (int i = 0; i < n; i++)
{
if (cmd[i].c[0] == 'E')
{
p pos3(r0, c0);
if (cmd[i].pos1 == pos3)
{
r0 = cmd[i].pos2.first;
c0 = cmd[i].pos2.second;
}
else if (cmd[i].pos2 == pos3)
{
r0 = cmd[i].pos1.first;
c0 = cmd[i].pos1.second;
}
}
else
{
int dr = 0, dc = 0;
for (int j = 0; j < cmd[i].a; j++)
{
int x = cmd[i].x[j];
if (cmd[i].c[0] == 'I')
{
if (cmd[i].c[1] == 'R' && x <= r0) dr++;
else if (cmd[i].c[1] == 'C' && x <= c0) dc++;
}
else
{
if (cmd[i].c[1] == 'R')
{
if (x == r0) return 0;
if (x < r0) dr--;
}
else
{
if (x == c0) return 0;
if (x < c0) dc--;
}
}
}
r0 += dr; c0 += dc;
}
}
return 1;
}
int main()
{
int r0, c0, q, kase = 0;
while (cin >> r >> c >> n && r)
{
for (int i = 0; i < n; i++)
{
cin >> cmd[i].c;
if (cmd[i].c[0] == 'E') cin >> cmd[i].pos1.first >> cmd[i].pos1.second >> cmd[i].pos2.first >> cmd[i].pos2.second;
else
{
cin >> cmd[i].a;
for (int j = 0; j < cmd[i].a; j++)
cin >> cmd[i].x[j];
}
}
if (kase) cout << endl;
cout << "Spreadsheet #" << ++kase << endl;
cin >> q;
while (q--)
{
cin >> r0 >> c0;
cout << "Cell data in (" << r0 << "," << c0 << ") ";
if (!simulate(r0, c0)) cout << "GONE" << endl;
else cout << "moved to (" << r0 << "," << c0 << ")" << endl;
}
}
return 0;
}