P1477 [NOI2008] 假面舞会 题解
water_tomato · · 题解
本文同步发表于个人博客:Link。
题目链接。
发现题解区说大多都是直接说再连一条边权为
解析
首先发现若图中没有环,最大答案为所有联通块的最长链长度之和,最小答案为
若存在环,则最大答案为所有环的
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);
}
}
接着我们观察下面这张图。
我们发现,当图中出现上图这个情况时,记
也就是说,这个做法想要正确,需要有
先证明前者:
- 若
b \ge c ,有\gcd(a,|c-b|)=\gcd(a,b-c)=\gcd(a,a+b-c) ,显然正确 - 若
b<c ,有\gcd(a,|c-b|)=\gcd(a,c-b)=\gcd(a,a+c-b) ,换言之,我们需要证明\gcd(a,a+b)=\gcd(a,a-b),a>b 。
证明:
记
设
若
可设
故
再证明后者:
- 若
c \ge b ,有\gcd(a+b-c,|c-b|)=\gcd(a+b-c,c-b)=\gcd(a+b-c,a) 。 - 若
c<b ,有\gcd(a+b-c,|c-b|)=\gcd(a+b-c,b-c)=\gcd(a+b-c,a+2b-2c) ,将a+b-c 和b-c 分别看作\gcd(a,a+b)=\gcd(a,a-b) 中的a 和b ,由上得证。
同时我们又发现,除了上图的情况,其他的情况搜到的环的长度都是真是的或是可以化成上图情况的。
因此,建反向的边权为
代码
#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;
}