CF863C 1-2-3

发布时间 2023-10-16 10:12:21作者: LHLeisus

原题链接

题目给出了不同状态之间的转化关系,想到可以构成一张有向图,在 \((a,b)\)\((A_{a,b},B_{a,b})\) 之间连边。但是 \(k\) 的数量级十分巨大,直接跑 \(k\) 个点计算答案肯定是不行的。
事实上状态数最多只有 \(9\) 种,也就是最多只有 \(9\) 个点,图上一定存在一个环。我们只需要把图拆成两部分,一部分是环外的链(可能没有),一部分是环。链上的点直接统计;对于环上的点,先算出循环一圈的贡献,再计算一共循环多少圈以及最后不满一圈还需要额外走几步。跑一圈最多走 \(9\) 个点,直接统计即可。
连边其实没有必要,只需记录每个状态对应的编号。从 \(1\) 开始,对于第 \(i\) 个状态 \((a,b)\),若 \((A_{a,b},B_{a,b})\) 的编号小于 \(i+1\),就找到了环。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<utility>
#include<vector>
#include<queue>
#include<bitset>
#include<map>
#define int long long
#define FOR(i,a,b) for(register int i=a;i<=b;i++)
#define ROF(i,a,b) for(register int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
inline int read();
typedef long long ll;
typedef double db;
const int N=1e5+5;
const int INF=0x3f3f3f3f;
int n,m,k;
int ans1=0,ans2=0;
map<pll,int>pos;
map<int,pll>pos2;
int Pos=0;
void toNum(int u,int v){
	if(pos.find(mp(u,v))==pos.end())
		pos[mp(u,v)]=++Pos,pos2[Pos]=mp(u,v);
}
int A[4][4],B[4][4];
int calcA(pll x){
	if(x.fi==x.se) return 0;
	if(x.fi==1&&x.se==3) return 1;
	if(x.fi==2&&x.se==1) return 1;
	if(x.fi==3&&x.se==2) return 1;
	return 0;
}
int calcB(pll x){
	if(x.fi==x.se) return 0;
	if(x.fi==3&&x.se==1) return 1;
	if(x.fi==1&&x.se==2) return 1;
	if(x.fi==2&&x.se==3) return 1;
	return 0;
}
signed main()
{
	k=read();
	int a=read(),b=read();
	FOR(i,1,3) FOR(j,1,3) A[i][j]=read();
	FOR(i,1,3) FOR(j,1,3) B[i][j]=read();
	int temp=0;//环和链的分界点 
	while(1){
		if(pos.find(mp(A[a][b],B[a][b]))!=pos.end()){
			toNum(a,b),toNum(A[a][b],B[a][b]);
			temp=pos[mp(A[a][b],B[a][b])];
			break;
		}
		toNum(a,b),toNum(A[a][b],B[a][b]);
		int t=a;
		a=A[a][b];
		b=B[t][b];
	}
	FOR(i,1,temp-1){
		ans1+=calcA(pos2[i]);
		ans2+=calcB(pos2[i]);
		k--;
		if(k<=0) break;//走不到环,要及时退出 
	}
	int f=0,loop=0,ans1l=0,ans2l=0,re;
	FOR(i,1,Pos){
		if(i<temp) continue;
		if(i==temp) f++;
		if(f==2) break;
		loop++;
		ans1l+=calcA(pos2[i]);
		ans2l+=calcB(pos2[i]);
	}
	re=k%loop;
	ans1l=ans1l*(k/loop);
	ans2l=ans2l*(k/loop);
	FOR(i,1,Pos){
		if(i<temp) continue;
		if(re==0) break;
		re--;
		ans1l+=calcA(pos2[i]);
		ans2l+=calcB(pos2[i]);
	}
	printf("%lld %lld",ans1+ans1l,ans2+ans2l);
	return 0;
}


inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return f*x;
}