【题解】[ABC306G] Return to 1(数论)

【题解】[ABC306G] Return to 1

题目链接

ABC306G - Return to 1

题意概述

本题多测,TT 组数据。

对于每组数据,给定一个 nn 个点 mm 条边的有向图,无重边自环。

问从顶点 11 出发,能否恰好走 1010100{10^{10}}^{100} 步回到 11

数据范围

  • 1T,N,M2×1051≤T,N,M≤2\times 10^5
  • N2×105,M2×105\sum N \le 2 \times 10^5,\sum M \le 2 \times 10^5

思路分析

首先,可以删掉图中无法从顶点 11 出发到达的点,和无法到达顶点 11 的点,因为无论怎么走都不可能经过这些点。那么删掉后的图是强联通的。


LL 表示图中所有经过 11 的环长集合,ddLL 中所有数的 gcd\gcd。那么若从 11 出发能恰好走 1010100{10^{10}}^{100} 步回到 11 当且仅当:dd1010100{10^{10}}^{100} 的约数。

证明:

由于我们要从开始恰好走 1010100{{10}^{10}}^{100} 步回到 11。假设原图中有两个长度为 aabb 的环,那么就相当于找两个非负整数 xxyy,使得 ax+by=cax+by=c,其中 c=1010100c={{10}^{10}}^{100}

那么问题就转化为:对于方程 ax+by=cax+by=c,它有解的充要条件当且仅当 dcd|c,其中 d=gcd(a,b)d=\gcd(a,b)

充分性:根据裴蜀定理可知:存在 x0,y0x_0,y_0 使得 ax0+by0=dax_0+by_0=d ,又 dcd|c,所以 c=dk=(ax0+by0)k=a(kx0)+b(ky0)c=dk=(ax_0+by_0)k=a(kx_0)+b(ky_0),所以方程有整数解 (kx0,ky0)(kx_0,ky_0)

必要性:因为 ax0+by0=cax_0+by_0=cdda,ba,b 的最大公约数,所以 da,dbd|a,d|b ,所以 d(ax0+by0)d|(ax_0+by_0),即 dcd|c

更一般地,当原图中环的个数超过两个时,我们可以用归纳法证明出对于 LL 里所有元素 a1ana_1-a_n,方程 a1x1+a2x2+a3x3++anxn=ca_1x_1+a_2x_2+a_3x_3+\cdots+a_nx_n=c 有解当且仅当 gcd(a1,a2,,an)c\gcd(a_1,a_2,\cdots,a_n)|c

那么我们现在就可以有一个清晰的思路:暴力把所有的环长求出来,取它们的 gcd\gcd,判断其是否为 1010100{{10}^{10}}^{100} 的约数。

但是当经过 11 的环很多时,这种方法复杂度显然接受不了,所以考虑优化。


走到这一步,问题实际上可以转化为:

给定若干个经过 11 的环,如何求出这些环长的 gcd\gcd

我们考虑反向思考,首先要弄明白一个问题:对于一个数 xx,怎么判断它是否是所有经过 11 环长的公约数(注意这里不是最大)?

有一个方法是:从 11 开始 dfs 一遍所有点计算出每个点 ii11 的距离 depidep_i,那么要使得如果 xx 是所有环长的公约数,则在 modx\bmod x 意义下,对于图中的每条边 uvu\rightarrow v 都满足 depu+1=depvdep_u+1=dep_v

证明:

首先考虑简单环:

  • 如果给定一堆简单环,我们从 11 开始 dfs 给每个点标记一个模 xx 意义下的 depdep,那么每个环一定都是按照一定顺序像 0,1,2,,x1,0,1,0,1,2,\cdots ,x-1,0,1,\cdots 这样标号的。
  • 假设一个环环长为 lenlen,那么从 11 开始 dfs,到第 len1len-1 个点的过程中,对于非第 len1len-1 个点,无论如何一定有 depu+1=depvdep_u+1=dep_v,这是显然的。
  • 对于第 len1len-1 个点当且仅当它的 depdep 不是 x1x-1 时,这个点的编号加一模 xx 不等于 dep1=0dep_1=0,此时 xx 一定就不是该环环长的约数,与“xx 是所有环长公约数”矛盾。

然后考虑复杂环:

  • 复杂环与简单环的最主要区别就在于简单环一定不会存在公用边,也就是不会多个环经过同一条边的情况,而复杂环可能会出现。

  • 假设有两个环,环长均是 xx 的约数,从 11 出发经过不同的路径到达一个点 uu,然后再沿着同一条从 uu11 路径回到 11。比如下图(两个环分别从 11 出发沿着不同路径到达 33 再沿着相同的路径从 33 回到 11)。定义这个 uu 为两个环的交汇点len1len_1len2len_2 为两个环环长。

  • 那么对于两个环非交汇点,同样无论如何一定有 depu+1=depvdep_u+1=dep_v(与简单环相同)。

  • 对于两个环交汇点 uu(相当于图中的 33),如果存在其中一个环的一条边 vuv\rightarrow u,使得模 xx 意义下不满足 depv+1=depudep_v+1= dep_u,那么当且仅当这两个环的环长差 len1len2|len_1-len_2| 不是 xx 的倍数。

  • 由于 xxlen1len_1len2len_2 的公约数,所以 gcd(len1,len2)x\gcd(len_1,len_2)|x。根据更相减损法,有 gcd(len1,len2)=gcd(len1,len1len2)\gcd(len_1,len_2)=\gcd(len_1,|len_1-len_2|),所以 gcd(len1,len1len2)x\gcd(len_1,|len_1-len_2|)|x,所以 len1len2|len_1-len_2| 一定是 xx 的倍数。

img

由于 xx 是所有环长的公约数时,在 modx\bmod x 意义下,对于图中的每条边 uvu\rightarrow v 都满足 depu+1=depvdep_u+1=dep_v,即 depu+1depv0(modx)dep_u+1-dep_v\equiv 0 \pmod {x},所以 xx 是所有 depu+1depvdep_u+1-dep_v 的公约数。

同理,要使得 xx 是所有环长的 gcd\gcd,那么它也应该是所有 depu+1depvdep_u+1-dep_vgcd\gcd

那么我们直接枚举所有边 uvu \rightarrow v 求出 depu+1depvdep_u+1-dep_vgcd\gcd 即可。

最后一步就是要判断这个 gcd\gcd 是不是 1010100{{10}^{10}}^{100} 的约数。

由于 1010100{{10}^{10}}^{100} 只有 2255 两个质因数,且 1010100{{10}^{10}}^{100} 很大,所以只需要判断 gcd\gcd 是否只有 2255 两个质因子,若是,则答案 Yes,反之是 No


总体来说,这个题大的方面分为三步:先删掉图中无关的点;再将问题转化为求所有环长的 gcd\gcd;最后求这些环长 gcd\gcd

总复杂度 O(Tmax(n,m))O(T\max(n,m))

注意多测清空。

代码实现

//G
//The Way to The Terminal Station…
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#define mk make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=2e5+10;
int ok[maxn],dep[maxn];//ok[i] 表示 i 是否为有用点(即能不能到达 1)。

basic_string<int>edge[maxn],edge2[maxn];

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*10+ch-48;ch=getchar();}
	return x*f;
}

void dfs(int x)
{
	ok[x]=1;
	for(int y:edge2[x])
	{
		if(ok[y])continue;
		dfs(y);
	}
}

void dfs2(int x)
{
	for(int y:edge[x])
	{
		if(!ok[y])continue;
		if(dep[y]!=-1)continue;
		dep[y]=dep[x]+1;
		dfs2(y);
	}
}

int gcd(int a,int b){if(b==0)return a;return gcd(b,a%b);}

int main()
{
	int T=read();
	while(T--)
	{
		int n,m;
		n=read();m=read();
		for(int i=1;i<=n;i++)edge[i].clear(),edge2[i].clear(),dep[i]=-1,ok[i]=0;
		for(int i=1;i<=m;i++)
		{
			int u,v;
			u=read();v=read();
			edge[u]+=v;
			edge2[v]+=u;
		}
		dfs(1);
		dep[1]=0;
		dfs2(1);
		int d=0;
		for(int u=1;u<=n;u++)
		{
			if(dep[u]==-1)continue;
			for(int v:edge[u])
			{
				if(dep[v]==-1)continue;
				d=gcd(d,abs(dep[u]+1-dep[v]));
			}
		}
		if(!d){cout<<"No"<<'\n';continue;}//没有经过 1 的环。
		while(d%2==0)d/=2;
		while(d%5==0)d/=5;
		if(d==1)cout<<"Yes"<<'\n';
		else cout<<"No"<<"\n";
	}
	return 0;
}
posted @   向日葵Reta  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2022-06-18 CF 绿蓝题做题记录
点击右上角即可分享
微信分享提示