peiwenjun's blog 没有知识的荒原

P1477 [NOI2008] 假面舞会 题解

题目描述

\(k\ge 3\) 类面具,戴第 \(i\) 类面具的人能看到戴第 \(i\bmod k+1\) 类面具的人的编号。

给定 \(n\) 个人, \(m\) 条信息,每条信息形如编号为 \(u\) 的人能看到编号为 \(v\) 的人。

求 \(k\) 的最大、最小值,无解输出 -1 -1 。

数据范围

  • \(1\le n\le 10^5,0\le m\le 10^6\) 。

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{125MB}\) 。

分析

将每条信息连边 \((u,v,1),(v,u,-1)\) ,则整张图会形成若干连通块:

  • 如果有环,那么 \(k\) 为环长的约数。
  • 如果无环,则该连通块为一条链, \(k\) 不超过所有链长度之和。

任选一个点开始 \(dfs\) ,对每个点 \(u\) ,记录第一次访问该点时的距离 \(dis_u\) 。此后每次 \(dfs\) 到这个点,如果当前距离为 \(cur\) ,则意味着有一个长为 \(cur-dis_u\) 的环。

如果无环,对于一条 \(u\to v\) 的链,链长为 \(dis_v-dis_u\) 。记录最小和最大的 \(dis_u\) ,分别记为 \(mn\) 和 \(mx\) ,则最长链长度为 \(mx-mn+1\) 。

时间复杂度 \(\mathcal O(n\log n+m)\) 。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,maxm=2e6+5,inf=1e9;
int m,n,u,v,mn,mx,cir,res,tot;
int head[maxn],to[maxm],val[maxm],nxt[maxm];
int dis[maxn];
bool vis[maxn];
void addedge(int u,int v,int w)
{
    nxt[++tot]=head[u],to[tot]=v,val[tot]=w,head[u]=tot;
}
int gcd(int a,int b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
void dfs(int u,int cur)
{
    if(vis[u])
    {
        cir=gcd(abs(dis[u]-cur),cir);
        return ;
    }
    vis[u]=true,dis[u]=cur,mn=min(mn,cur),mx=max(mx,cur);
    for(int i=head[u];i!=0;i=nxt[i]) dfs(to[i],cur+val[i]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v,1),addedge(v,u,-1);
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]) continue;
        mn=inf,mx=-inf,dfs(i,0),res+=mx-mn+1;
    }
    if(cir)
    {
        if(cir<=2) printf("-1 -1");
        else for(int i=3;i<=cir;i++)
            {
                if(cir%i) continue;
                printf("%d %d",cir,i);
                break;
            }
    }
    else
    {
        if(res<=2) printf("-1 -1");
        else printf("%d %d\n",res,3);
    }
    return 0;
}

posted on 2022-06-08 17:30  peiwenjun  阅读(20)  评论(0)    收藏  举报

导航