[Luogu P5676][GZOI2017]小z玩游戏

\(GZOI2017D1T2\)

题目链接?不存在的 Luogu P5676 [GZOI2017]小z玩游戏

题面

做了半天一直不对,交一发就A了?样例出锅了吧。。(Luogu的样例是改过的,如果有错请联系我qwq)

很显然对每个兴奋程度向能玩的游戏连边,游戏向相应的兴奋程度连边,然后问题转化为有多少游戏在一个环上,且能被兴奋程度为\(1\)的节点到达

但是直接连边是\(O(n^2)\)的,显然不行,需要优化。

\(N=100000\)

\(N\)个点\(1\sim N\)表示当前兴奋程度

\(N\)个点\(N+1\sim 2N\)为中转节点

\(N\)个点\(2N+1\sim 3N\)表示\(N\)款游戏

那么对于\(1\le i,j\le N,i|j\),连边\((i,N+j)\),表示兴奋程度\(i\)能玩有趣程度为\(j\)的游戏

对于\(1\le i\le n\),连边\((N+w_i,2N+i),(2N+i,e_i)\),表示第\(i\)款游戏有趣程度为\(w_i\),玩完后兴奋程度变为\(e_i\)

那么连边的复杂度就是喜闻乐见的调和级数:\(\sum_{i=1}^N\limits \frac Ni=O(N\log N)\)

接着跑一遍Tarjan,DFS一边求出哪些强连通分量可以被初始点达到

然后判断一下就好了,注意大小为\(1\)的强连通分量不是环。

时间复杂度 \(O(TN\log N)\)

空间复杂度 \(O(N\log N)\)

代码:

#include <cstdio>
#include <cctype>
#include <cstring>

inline int Min(const int a,const int b){return a<b?a:b;}
inline int Max(const int a,const int b){return a>b?a:b;}
#define Getchar (p1==p2&&(p2=(p1=In)+fread(In,1,1<<22,stdin),p1==p2)?EOF:*p1++)
char In[1<<22],*p1=In,*p2=In,Ch;
inline int Getint(int x=0)
{
    while(!isdigit(Ch=Getchar));
    for(;isdigit(Ch);Ch=Getchar)x=x*10+(Ch^48);
    return x;
}

const int N=100000,V=N*3+5,E=N*14;
int T,n,w[N+5],Ans;
int Dfn[V],Low[V],Tim,Ins[V],Sta[V],Top;
int Dcc[V],Siz[V],Dn,Deg[V],Vis[V];
struct Graph
{
    int Head[V],Next[E],To[E],En;
    inline void Clear(){memset(Head,En=0,sizeof Head);}
    inline void Add(int x,int y){Next[++En]=Head[x],To[Head[x]=En]=y;}
}G1,G2;

void Tarjan(int x)
{
    Dfn[x]=Low[x]=++Tim,Ins[Sta[++Top]=x]=1;
    for(int i=G1.Head[x],y;i;i=G1.Next[i])
        if(!Dfn[y=G1.To[i]])Tarjan(y),Low[x]=Min(Low[x],Low[y]);
        else if(Ins[y])Low[x]=Min(Low[x],Dfn[y]);
    if(Low[x]==Dfn[x])
    {
        int y=++Dn;
        do Ins[y=Sta[Top--]]=0,Dcc[y]=Dn,++Siz[Dn];
        while(y!=x);
    }
}

void DFS(int x){if(!Vis[x]){Vis[x]=1;for(int i=G2.Head[x];i;i=G2.Next[i])DFS(G2.To[i]);}}

int main()
{
    freopen("in.txt","r",stdin);
    for(T=Getint();T--;printf("%d\n",Ans))
    {
        G1.Clear(),G2.Clear(),n=Getint(),Ans=0;
        for(int i=1;i<=N;++i)
            for(int j=i;j<=N;j+=i)
                G1.Add(i,N+j);
        for(int i=1;i<=n;++i)G1.Add(N+(w[i]=Getint()),N*2+i);
        for(int i=1;i<=n;++i)G1.Add(N*2+i,Getint());
        memset(Dfn,0,sizeof Dfn),memset(Siz,0,sizeof Siz),Tim=Dn=0;
        for(int i=1;i<=3*N;++i)if(!Dfn[i])Tarjan(i);
        memset(Deg,0,sizeof Deg),memset(Vis,0,sizeof Vis);
        for(int i=1,t;i<=3*N;++i)
            for(int j=G1.Head[i];j;j=G1.Next[j])
                if(Dcc[i]!=(t=Dcc[G1.To[j]]))
                    G2.Add(Dcc[i],t),++Deg[t];
        DFS(Dcc[1]);
        for(int i=1;i<=n;++i)
            if(Vis[Dcc[N*2+i]]&&Siz[Dcc[N*2+i]]>1)
                ++Ans;
    }
    return 0;
}
posted @ 2019-11-20 15:06  LanrTabe  阅读(188)  评论(0编辑  收藏  举报