bzoj 1064 假面舞会

来源:互联网 发布:手机紫外线灯软件 编辑:程序博客网 时间:2024/04/29 01:46

这是一道不错的题,膜拜题解

一个很巧妙的处理:搜索拓扑图最长链时,设反向边的长度是-1,那么任选一个节点开始dp就能获得正确答案。

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N 100010
#define M 1000010
using namespace std;
struct yts { int x,t,l,ne;} e[2*M];
bool vis[N];
int v[N],dis[N];
int ans=0,mn,mx,num=0;
int gcd(int a,int b) { return b==0?a:gcd(b,a%b); }
void put(int x,int y,int l)
{
 num++; e[num].x=x; e[num].t=y; e[num].l=l;
 e[num].ne=v[x]; v[x]=num;
}
void dfs(int x)
{
 vis[x]=1;
 for (int i=v[x];i;i=e[i].ne)
 {
  int y=e[i].t;
  if (vis[y])
  {
   int len=abs(dis[y]-dis[x]-e[i].l);
   ans=ans==0?len:gcd(ans,len);
  }
  else
  {
   vis[y]=1; dis[y]=dis[x]+e[i].l;
   dfs(y);
  }
 }
}
void dp(int x)
{
 vis[x]=1;
 mn=min(mn,dis[x]);
 mx=max(mx,dis[x]);
 for (int i=v[x];i;i=e[i].ne)
 {
  int y=e[i].t;
  if (!vis[y])
  {
   vis[y]=1; dis[y]=dis[x]+e[i].l;
   dp(y);
  }
 }
}
int main()
{
 int n,m;
 scanf("%d%d",&n,&m);
 for (int i=1;i<=m;i++)
 {
  int x,y;
  scanf("%d%d",&x,&y);
  put(x,y,1); put(y,x,-1);
 }
 for (int i=1;i<=n;i++) if (!vis[i]) dfs(i);
 int mnans;
 if (ans)
 {
  if (ans<=2) ans=mnans=-1;
  else
  {
   mnans=3;while (mnans<=ans&&ans%mnans) mnans++;
  }
 }
 else
 {
  memset(vis,0,sizeof(vis));
  memset(dis,0,sizeof(dis));
  for (int i=1;i<=n;i++)
    if (!vis[i])
    {
     mn=mx=0;
     dp(i);
     ans+=mx-mn+1;
    }
  mnans=3;
  if (ans<3) mnans=ans=-1;
 }
 printf("%d %d\n",ans,mnans);
 return 0;
}


0 0