【JZOJ 6518】Left Out
题目大意:
有 \(n\cdot n\) 头牛,有的牛朝左边,有的牛朝右边,可以将某一行或某一列的奶牛全部往后转(即朝左的向右,朝右的向左),现在让所有奶牛中除了一头之外都朝向相同的方向,请找出这样的一头奶牛。
正文:
举例:
直接这么推会比较难,那就把思路倒过来,假设捣乱的那头牛的位置在 \([1,1]\),因为方便我就设 L 为 \(1\),R 为 \(0\):
011
111
111
就像这样,除了那头牛,其他的牛都往一个方向看。如果我们要把上面的 01 矩阵变为样例的,应该怎么变?
很容易,因为一个操作是将某一行或一列取反,那某一行和某一列的交叉处,就是不更改方向的牛。既然这样,那把上面的 01 矩阵变为样例的,可以这样:
↓
011 010
→111 → 001
111 110
其实可以发现,样例矩阵把每一个第一个数是 \(0\) 的行取反(第一行除外),再把每一个第一个数是 \(0\) 的列取反(第一列除外),就能得到原先的 01 矩阵。
解决问题:
那我们换一个想法,将样例矩阵的每一个第一个数是 \(1\) 的行取反,再把每一个第一个数是 \(1\) 的列取反,会怎样呢?
最终的 01 矩阵是这样:
000
011
011
可以发现除了第一行和第一列,其他数字都是 \(1\),根据这个特性,就能解答这题了。
答案分为三种类型:
- 捣乱的牛在左上角,即 \([1,1]\)。
- 捣乱的牛在第一行或第一列(左上角除外),即 \([1,x]\) 或 \([x,1] (1<x\leq n)\)。
- 其他。
第二和第三种的最终 01 矩阵分别是:
000
010
010
(第二种,[1,2])
可以发现第二种类型有 \((n-1)\) 个 \(1\)。
000
001
000
(第三种,[2,3])
可以发现捣乱的牛所在的位置就是 \(1\),其他都是 \(0\)。
代码:
const int N = 1010;
int n;
int a[N][N], b;
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
char c;
c = getchar();
while(c != 'L' && c != 'R')
c = getchar();
for (int j = 1; j <= n; j++)
{
a[i][j] = c=='L'?1:0;
c = getchar();
}
}
for (int i = 1; i <= n; i++)
if(a[i][1])
for (int j = 1; j <= n; j++)
a[i][j] ^= 1;
for (int i = 2; i <= n; i++)
if(a[1][i])
for (int j = 1; j <= n; j++)
a[j][i] ^= 1;
for (int i = 2; i <= n; i++)
for (int j = 2; j <= n; j++)
b += a[i][j];
if(b == (n-1) * (n-1))
{
if(n == 2) puts("-1");
else puts("1 1");
return 0;
}
if(b == 1)
{
for (int i = 2; i <= n; i++)
for (int j = 2; j <= n; j++)
if(a[i][j])
{
printf("%d %d", i, j);
return 0;
}
}
if(b == n-1)
{
int c = 0;
for (int i = 1; i <= n; i++)
{
c = 0;
for (int j = 1; j <= n; j++)
c += a[i][j];
if(c == b)
{
printf("%d 1", i);
return 0;
}
}
c = 0;
for (int i = 1; i <= n; i++)
{
c = 0;
for (int j = 1; j <= n; j++)
c += a[j][i];
if(c == b)
{
printf("1 %d", i);
return 0;
}
}
}
return 0;
}