[NOI2008]假面舞会 (搜索+gcd)
题意
题解
对于每一条边,建两条边。跑,如果这个点已经来过,就把到当前的距离与已经得到的值的差存起来,所有的值取一个就是最大的答案,最小的答案枚举一下的因数就行了。如果就无解。
这是有环的情况,没有环的情况,最大的答案就是每个连通块的最长链长度加起来。最小答案是。如果最长链的和就无解。
CODE
#pragma GCC optimize (3)
#include <bits/stdc++.h>
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch; for(;!isdigit(ch=getchar()););
for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}
typedef long long LL;
const int MAXN = 100005;
const int MAXM = 1000005;
int n, stk[MAXN], indx, m; bool vis[MAXN];
int fir[MAXN], to[MAXM<<1], nxt[MAXM<<1], wt[MAXM<<1], cnt;
inline void link(int u, int v, int w) {
to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; wt[cnt] = w;
}
int q[MAXN], s, t, dis[MAXN];
vector<int>vec;
int tot;
void bfs(int S) {
s = 0, t = 0; bool flg = 0;
vis[S] = 1; dis[S] = m; q[t++] = S;
int Mn = m, Mx = m;
while(s < t) {
int u = q[s++];
for(int i = fir[u], v; i; i = nxt[i])
if(!vis[v=to[i]]) {
vis[v] = 1;
dis[v] = dis[u] + wt[i];
Mn = min(Mn, dis[v]);
Mx = max(Mx, dis[v]);
q[t++] = v;
}
else {
if(dis[v] != dis[u] + wt[i])
flg = 1, vec.push_back(abs(dis[u]+wt[i]-dis[v]));
}
}
if(!flg) tot += Mx - Mn + 1;
}
int main () {
read(n), read(m);
for(int i = 1, u, v; i <= m; ++i) read(u), read(v), link(u, v, 1), link(v, u, -1);
for(int i = 1; i <= n; ++i) if(!vis[i]) bfs(i);
if(vec.size()) {
int gcd = 0;
for(int i = vec.size()-1; i >= 0; --i)
gcd = __gcd(gcd, vec[i]);
if(gcd <= 2) puts("-1 -1");
else {
int ans = gcd;
for(int i = 3; i <= gcd; ++i)
if(gcd % i == 0) { ans = i; break; }
printf("%d %d\n", gcd, ans);
}
}
else {
if(tot <= 2) puts("-1 -1");
else printf("%d 3\n", tot);
}
}