Luogu P1477 [NOI2008]假面舞會

一道很是神奇的圖論題解法無比新奇清新git

咱們首先把圖分紅三種狀況:spa

  1. 有環的,此時答案必定是環長的因數(不然不能知足題意)
  2. 存在入度大於1的DAG圖的
  3. 一棵樹/一條鏈

很容易發現,最後一種狀況想怎麼取就怎麼取,那麼咱們只須要考慮1,2的影響便可code

可是若是咱們傻乎乎地直接跑好像也是能夠的那就太煩了。blog

咱們考慮這樣的一個圖:string

咱們把原圖中的邊分紅兩類:紅色(順時針)和綠色(逆時針)。而後咱們發現這種圖對應上面講的狀況2。而後手推顏色發現要兩種。it

而這個2是怎麼來的,很簡單,\(color=| total_{red}-total_{green} |\)。爲何,咱們仔細觀察一下對於每個被一條順時針邊和一條逆時針邊鏈接的點,與它相連的點顏色必定相同io

而後咱們就能夠把問題轉化成無向邊並找環了。可是圖中的邊並無順時針/逆時針,因而咱們分紅正向邊/反向邊考慮便可。class

能夠設爲相反的邊權(如\(1 \&\& -1\)等)。而後取絕對值便可。gc

而後對於有環的狀況(不管時1仍是2),咱們均可以得出\(ans=gcd(|len|)\),其中\(len\)表示環長,那麼最小值就是\(min(ans|u)\)im

值得注意的還有森林的狀況,稍加推到此時的\(ans=\sum maxdis-mindis+1 \ (for\ each\ Unicom\ blocks\ of \ the\ gragh)\)

最後當獲得的\(ans<3\)時無解。找環固然是BFS/DFS了(注意不要把SCC搞混了)

BFS版CODE

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=100005,M=1000005;
struct edge
{
    int to,next,v;
}e[M<<1];
int head[N],dis[N],q[N],n,m,x,y,ans,lans,sum,cnt,MIN,MAX;
bool vis[N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void double_add(int x,int y)
{
    e[++cnt].to=y; e[cnt].v=1; e[cnt].next=head[x]; head[x]=cnt;
    e[++cnt].to=x; e[cnt].v=-1; e[cnt].next=head[y]; head[y]=cnt;
}
inline int gcd(int n,int m)
{
    return m?gcd(m,n%m):n;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
inline void BFS(int s)
{
    register int i,H=0,T=1; vis[s]=1; q[1]=s;
    while (H<T)
    {
        int now=q[++H];
        for (i=head[now];~i;i=e[i].next)
        if (vis[e[i].to]) ans=gcd(ans,dis[now]-dis[e[i].to]+e[i].v); else
        {
            dis[e[i].to]=dis[now]+e[i].v; vis[e[i].to]=1; q[++T]=e[i].to;
            MIN=min(MIN,dis[e[i].to]); MAX=max(MAX,dis[e[i].to]);
        }
    }
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(n); read(m);
    memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e));
    for (i=1;i<=m;++i)
    read(x),read(y),double_add(x,y);
    for (i=1;i<=n;++i)
    if (!vis[i])
    {
        MAX=MIN=0; BFS(i);
        sum+=MAX-MIN+1;
    }
    if (ans<0) ans=-ans;
    if (ans)
    {
        if (ans<3) return puts("-1 -1"),0;
        for (lans=3;lans<ans&&ans%lans;++lans);
        return printf("%d %d",ans,lans),0;
    }
    if (sum<3) return puts("-1 -1"),0;
    return printf("%d 3",sum),0;
}

DFS版CODE

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=100005,M=1000005;
struct edge
{
    int to,next,v;
}e[M<<1];
int head[N],dis[N],n,m,x,y,ans,lans,sum,cnt,MIN,MAX;
bool vis[N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void double_add(int x,int y)
{
    e[++cnt].to=y; e[cnt].v=1; e[cnt].next=head[x]; head[x]=cnt;
    e[++cnt].to=x; e[cnt].v=-1; e[cnt].next=head[y]; head[y]=cnt;
}
inline int gcd(int n,int m)
{
    return m?gcd(m,n%m):n;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
inline void DFS(int now)
{
    register int i; vis[now]=1;
    for (i=head[now];~i;i=e[i].next)
    if (vis[e[i].to]) ans=gcd(ans,dis[now]-dis[e[i].to]+e[i].v); 
    else MIN=min(MIN,dis[e[i].to]=(dis[now]+e[i].v)),MAX=max(MAX,dis[e[i].to]),DFS(e[i].to);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(n); read(m);
    memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e));
    for (i=1;i<=m;++i)
    read(x),read(y),double_add(x,y);
    for (i=1;i<=n;++i)
    if (!vis[i])
    {
        MAX=MIN=0; DFS(i);
        sum+=MAX-MIN+1;
    }
    if (ans<0) ans=-ans;
    if (ans)
    {
        if (ans<3) return puts("-1 -1"),0;
        for (lans=3;lans<ans&&ans%lans;++lans);
        return printf("%d %d",ans,lans),0;
    }
    if (sum<3) return puts("-1 -1"),0;
    return printf("%d 3",sum),0;
}
相關文章
相關標籤/搜索