CF1481D AB Graph 题解

CF1481D AB Graph 题解

【思路】

首先有几个显而易见的东西。

  1. 如果存在两个点,他们之间的两条边字母相同,那么一定有解(在两个点之间跳。)

  2. 否则,这张图的邻接矩阵一定长成这样:

    * a b a
    b * a b
    a b * a
    b a b *
    

    沿着中间的斜线看,左右两边完全相反。

然后找这种图的特点。

  • 如果 $ n=2 $

    • 如果 $ m $ 是奇数,有解。

    • 如果 $ m $ 是偶数,无解。

  • 如果 $ n=3 $,必然有解,证明放在最后。

所以说,对于所有 $ n \ge 3 $,我们直接选择前三个点即可。

【代码】

#include <bits/stdc++.h>
using namespace std;

char a[1005][1005];
int T,n,m;

int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			getchar();
			for(int j=1;j<=n;j++){
				scanf("%c",&a[i][j]);
			}
		}
		
		//如果存在两个点,他们之间的两条边字母相同,那么一定有解(在两个点之间跳。)
		bool flag_equ=false;
		int flag1=0,flag2=0;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				if(a[i][j]==a[j][i]){
					flag_equ=true;
					flag1=i;
					flag2=j;
					break;
				}
			}
		}
		if(flag_equ==true){
			puts("YES");
			for(int i=1;i<=m+1;i++){
				printf("%d ",i&1?flag1:flag2); 
			}puts("");
		}else{
			
			//如果 n=2
			if(n==2){
				if(m&1){
					puts("YES");
					for(int i=1;i<=m+1;i++){
						printf("%d ",i&1?1:2); 
					}puts("");
				}else{
					puts("NO");
				}
			}else{
				//如果 n>=3,必然有解。选择前三个点。 
				//如果 1 -> 2 -> 3 -> 1 相等,那么直接输出
				if(a[1][2]==a[2][3] and a[2][3]==a[3][1]){
					puts("YES");
					for(int i=1;i<=m+1;i++){
						printf("%d ",(i-1)%3+1); 
					}puts("");
				}else{
					puts("YES");
					//定位3个点.
					//P1 有两个a的出度,两个b的入度 
					int P1;
					if(a[1][2]==a[1][3] and a[1][3]=='a' and a[2][1]==a[3][1] and a[3][1]=='b') P1=1;
					if(a[2][1]==a[2][3] and a[2][3]=='a' and a[1][2]==a[3][2] and a[3][2]=='b') P1=2;
					if(a[3][1]==a[3][2] and a[3][2]=='a' and a[1][3]==a[2][3] and a[2][3]=='b') P1=3;
					
					//P2 有两个b的出度,两个a的入度 
					int P2;
					if(a[1][2]==a[1][3] and a[1][3]=='b' and a[2][1]==a[3][1] and a[3][1]=='a') P2=1;
					if(a[2][1]==a[2][3] and a[2][3]=='b' and a[1][2]==a[3][2] and a[3][2]=='a') P2=2;
					if(a[3][1]==a[3][2] and a[3][2]=='b' and a[1][3]==a[2][3] and a[2][3]=='a') P2=3;
					
					//P3 是剩下那一个点,入度出度都为一a一b 
					int P3;
					P3=6-P1-P2;
					
					
					if(m&1){          //如果 m 是奇数,那么不停跳 ab/ba 即可
						for(int i=1;i<=m+1;i++){
							printf("%d ",i&1?P1:P2);
						}puts("");
					}else if(m%4==0){ //如果 m 可以被 4 整除,那么不停跳 abba/baab 即可
						for(int i=1;i<=m+1;i++){
							if(i%4==1) printf("%d ",P3); 
							if(i%4==2) printf("%d ",P2); 
							if(i%4==3) printf("%d ",P3); 
							if(i%4==0) printf("%d ",P1);
						}puts("");
					}else{            //如果 m 不能被 4 整除,但是可以被 2 整除,那么先跳一个 a/b,再不停跳 abba/baab,最后再跳一个 a/b 即可
						printf("%d ",P1);
						for(int i=2;i<=m;i++){
							if((i-1)%4==1) printf("%d ",P3);
							if((i-1)%4==2) printf("%d ",P2);
							if((i-1)%4==3) printf("%d ",P3);
							if((i-1)%4==0) printf("%d ",P1);
						}printf("%d ",P2);
						puts("");
					} 
				}
			}
		}
		
	}
	return 0;
}

【证明】

对于 $ n=3 $ 的情况下,必然有解。

  1. 假设邻接矩阵是下面这几种情况:

       * a b   |   * b a
       b * a   |   a * b
       a b *   |   b a *
    

    那么只要不停地跳 $ a/b $ 即可,输出为 1 2 3 1 2 3 1 2 3 ...

  2. 假设邻接矩阵是下面这几种情况:

       * a a   |   * b b
       b * b   |   a * a
       b a *   |   a b *
    
    • 如果 $ m $ 是奇数,那么不停跳 $ ab/ba $ 即可,输出为 1 2 1 2 1 2 ...

    • 如果 $ m $ 被 $ 4 $ 整除,那么不停跳 $ abba/baab $ 即可,输出为 2 3 2 1 2 3 2 1 2 3 2 1 2...

    • 如果 $ m $ 不能被 $ 4 $ 整除,但是可以被 $ 2 $ 整除,那么先跳一个 $ a/b $,再不停跳 $ abba/baab $,最后再跳一个 $ a/b $ 即可,输出为 1 2 3 2 1 2 3 2 1 2 3 2 1 2...3 2 1 2 3

  3. 假设邻接矩阵是下面这几种情况:

       * a a   |   * b b   |   * a b   |   * b a
       b * a   |   a * b   |   b * b   |   b * a
       b b *   |   a a *   |   a a *   |   b a *
    

    套路和上边的一样,可以自己画图看看。

posted @ 2024-02-24 17:22  Sundar_2022  阅读(3)  评论(0编辑  收藏  举报