题解 CF863C 【1-2-3】
这道题目小心细节啊!!
写在前面的话:
我提交了大概16次才A掉这个题,前12次是因为没有特判循环节长度为0(相信有不少人RE是因为这个原因),也就是没有找到循环节,然后我用k去除以循环节的长度,就疯狂RE.
后面几次是因为不小心写挂了一个地方,导致死循环了。
这道题的解题思路:
观察到给出的两个矩阵的大小仅有\(3*3\),而且k的大小到了 \(10^{18}\),当然是找循环节了!很显然,出现的情况无非就是9种:
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
考虑用一个数组记录一下每种情况是否出现过,如果出现过,肯定是有循环节,对于循环到的每一种情况采用一个栈储存起来,然后找循环节就是不断退栈.
找到循环节把每个循环节(不超过9)的输赢情况记录下来,然后直接根据循环节长度计算就行了
注意特判循环节长度为\(0\)!!!RE12次血的教训,让人想哭。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long//这是一个坏习惯,但是我懒....
int a[5][5];//记录矩阵A
int b[5][5];//记录矩阵B
int k;
int A,B;
bool book[5][5];//用来记录每种情况是否出现
short tackA[25],tackB[25];//用来记录循环节
int tail = 0;
int sorceA = 0,sorceB = 0,len = 0,addA = 0,addB = 0;
int pd(int x,int y)
{
if(x == 3 && y == 2)return 1;
if(x == 2 && y == 1)return 1;
if(x == 1 && y == 3)return 1;
if(x == y)return -1;
return 0;
}//判断输赢,用了用异或的小技巧
signed main()
{
cin >> k >> A >> B;
for(int i = 1 ; i <= 3 ; i ++)
for(int j = 1 ; j <= 3 ; j ++)
cin >> a[i][j];
for(int i = 1 ; i <= 3 ; i ++)
for(int j = 1 ; j <= 3 ; j ++)
cin >> b[i][j];
book[A][B] = 1;
int z = pd(A,B);
if(z != -1)sorceA += z , sorceB += z^1;
k --;tail ++ , tackA[tail] = A,tackB[tail] = B;//先把初始情况入队
while(k)//这里是找循环节
{
int x = a[A][B] , y = b[A][B];
tail ++ , tackA[tail] = x, tackB[tail] = y;
if(book[x][y] == 1)break;//如果出现了重复的情况显然就出现了循环
book[x][y] = 1;
int z = pd(x,y);
if(z != -1)sorceA += z , sorceB += z^1;//每次判断输赢
A = x , B = y;//记录上一次的结果
k --;
}
int X = tackA[tail],Y = tackB[tail];
tail --;
while(tail)// 退栈
{
len ++;
int z = pd(tackA[tail],tackB[tail]);
if(z != -1)addA += z , addB += z^1;
if(tackA[tail] == X && tackB[tail] == Y)break;
tail --;//找循环节
}
if(len != 0){
sorceA += (k/len)*addA , sorceB += (k/len)*addB;
k %= len;
}
while(k > 0)//此时的k一定小于循环节
{
int X = tackA[tail] , Y = tackB[tail];
int z = pd(X,Y);
if(z != -1)sorceA += z , sorceB += z^1;
k --;
tail ++;//我退了栈,实际上元素还待在栈里面,我回溯找回元素来计算
}
cout << sorceA << " " << sorceB;
return 0;
}
By MYCui