ABC306G Return to 1 题解
题意
- 给你一张有
N 个结点M 条边的有向图,问能不能经过恰好10^{10^{100}} 条边从结点1 出发回到结点1 ,可以重复经过同一个点或边。 -
题解
首先看到
但这个思路本身有点小问题,比如这种情况:
容易想到只需要重复走
关于如何解决这个问题,这个我们放到最后讲。
刚刚提过这个问题和数论有关,不妨把它放到一个代数的情景下,就转化成了这个问题:
求证
a_1x_1+a_2x_2+\dots+a_Kx_K=10^{10^{100}} 是否有正整数解。
多元一次不定方程有解性证明让我们联想到什么?裴蜀定理!
简单讲一下裴蜀定理:
不定方程
ax+by=c(a,b,c\in \mathbb{Z}) 有整数解当且仅当\gcd(a,b)\mid c 。
这里讲一个我比较喜欢的证明(主要是其他的我看不懂)。
设集合
假设集合
接下来证明
首先,设
然后,当取到
综上,
然后这个证明还可以拓展到多个数(比如 我邀请读者把这当做一种练习。
拓展到多个数得到的结论就是这道题要用的了,注意到
注意到裴蜀定理只能保证有整数解,我们还需要证明它有正整数解。
首先有个比较经典的结论:
对于不定方程
ax+by=c (a,b,c\in \mathbb{Z},\gcd(a,b)=1 ),它有正整数解的充分不必要条件是c> ab 。
这一部分类似于 P3951 [NOIP2017 提高组] 小凯的疑惑,我还是简单证明一下。
设不定方程
其中
回到原来的证明,设
假如
另外我们可以得到
由通解的那个式子容易发现我们可以把
此处我们用
首先可以列出方程
接下来同理,得到
那么
其实也可以感性理解一下,这个数太大了,也就是说只要能凑出某个只含有质因子
现在难点都证明完了,我们考虑如何统计所有环的长度。
首先,我们不能直接用 dfs 统计,前面已经说过了。
考虑用 dfs 随便建一棵外向树
最近 csacademy.com 炸了将就一下。
在上图中,黑色和绿色的箭头表示有向边,蓝色是生成的外向树
我们发现,假设存在一条边
这时候细心的读者就会发现问题了,比如左边那条边算出来就是
而下面的那条边,它算出来是
综上,整理一下整个实现过程:
- 分别建正反两无向图。
- 在正图上做 dfs 算出每个点和结点
1 在外向树的距离,在反图上做 dfs 判断某个点是否在内向树上。 - 枚举
u ,找到所有合法的v ,直接统计所有路径长度的\gcd 。 - 判断
\gcd 是否含有2 和5 以外的质因子,得到答案。
code
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define forup(i,s,e) for(int i=(s);i<=(e);++i)
#define fordown(i,s,e) for(int i=(s);i>=(e);--i)
using namespace std;
#define gc getchar()
int read(){//快读
int x=0,f=1;char c;
while(!isdigit(c=gc))if(c=='-')f=-1;
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc;}
return x*f;
}
const int N=2e5+5,inf=0x3f3f3f3f;
int t,n,m;
vector<int> e[N],re[N],nt[N];//正图,反图,不在外向树上的边
int ret[N],dpt[N];//能否回到 1,从 1 到该节点的距离
int vis[N];
void dfs(int x){//建外向树
vis[x]=1;
for(auto i:e[x]){
if(vis[i]){
nt[x].push_back(i);
continue;
}
dpt[i]=dpt[x]+1;
dfs(i);
}
}
void dfs1(int x){//建内向树
vis[x]=1;
for(auto i:re[x]){
if(vis[i]){
if(i==1) ret[1]=1;//特判结点 1
continue;
}
ret[i]=1;
dfs1(i);
}
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
signed main(){
t=read();
while(t--){
n=read();m=read();
forup(i,1,n){//注意清空
e[i].clear();
re[i].clear();
nt[i].clear();
}
forup(i,1,m){
int u=read(),v=read();
e[u].push_back(v);
re[v].push_back(u);
}
forup(i,1,n){
ret[i]=vis[i]=0;
dpt[i]=-1;
}
dpt[1]=0;
dfs(1);
forup(i,1,n){
vis[i]=0;
}
dfs1(1);
if(!ret[1]){//如果 ret[1]=0 说明整个图不存在包含结点 1 的环,无解。
puts("No");
continue;
}
int G=-1;
forup(i,1,n){
for(auto v:nt[i]){
if(!ret[v]||dpt[i]==-1) continue;
if(G==-1){//直接统计 gcd
G=abs(dpt[i]+1-dpt[v]);
}else{
G=gcd(G,abs(dpt[i]+1-dpt[v]));
}
}
if(G==1) break;//如果等于 1 就不可能再变小了,直接 break
//其实这里还可以加一些其它剪枝,但是我懒得搞
}
while(!(G%2)) G/=2;
while(!(G%5)) G/=5;//去掉所有质因子 2 和 5,判剩下的
puts(G==1?"Yes":"No");
}
}