题目链接。
发现题解区说大多都是直接说再连一条边权为
解析
首先发现若图中没有环,最大答案为所有联通块的最长链长度之和,最小答案为
若存在环,则最大答案为所有环的
inline void dfs(int u,int d){ if(dis[u]){ ans=gcd(ans,abs(d-dis[u])); return; } dis[u]=d;vis[u]=1; mx=max(mx,dis[u]);mn=min(mn,dis[u]); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;dfs(v,d+e[i].w); } }
接着我们观察下面这张图。
我们发现,当图中出现上图这个情况时,记
也就是说,这个做法想要正确,需要有
先证明前者:
- 若
,有 ,显然正确 - 若
,有 ,换言之,我们需要证明 。
证明:
记
设
若
可设
故
再证明后者:
- 若
,有 。 - 若
,有 ,将 和 分别看作 中的 和 ,由上得证。
同时我们又发现,除了上图的情况,其他的情况搜到的环的长度都是真是的或是可以化成上图情况的。
因此,建反向的边权为
代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; const int M=1e6+5; int n,m; struct node{ int to,nxt,w; }e[M]; int cnt,head[N]; inline void add(int u,int v,int w){ e[++cnt].to=v; e[cnt].nxt=head[u]; e[cnt].w=w; head[u]=cnt; } int dis[N],ans,ans2,mx,mn; bool vis[N]; inline int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } inline void dfs(int u,int d){ if(dis[u]){ ans=gcd(ans,abs(d-dis[u])); return; } dis[u]=d;vis[u]=1; mx=max(mx,dis[u]);mn=min(mn,dis[u]); for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;dfs(v,d+e[i].w); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); add(u,v,1);add(v,u,-1); } for(int i=1;i<=n;i++){ if(!vis[i]){ mx=-1e9;mn=1e9; dfs(i,1); ans2+=mx-mn+1;//找最长链 } } if(ans){//有环 if(ans<3) printf("-1 -1\n"); else for(int i=3;i<=ans;i++){ if(!(ans%i)){ printf("%d %d\n",ans,i); return 0; } } } else{//没环 if(ans2<3) printf("-1 -1\n"); else printf("%d 3\n",ans2); } return 0; }