(GZOI2017D1T2)
题目链接?不存在的 Luogu P5676 [GZOI2017]小z玩游戏
做了半天一直不对,交一发就A了?样例出锅了吧。。(Luogu的样例是改过的,如果有错请联系我qwq)
很显然对每个兴奋程度向能玩的游戏连边,游戏向相应的兴奋程度连边,然后问题转化为有多少游戏在一个环上,且能被兴奋程度为(1)的节点到达
但是直接连边是(O(n^2))的,显然不行,需要优化。
设(N=100000)
建(N)个点(1sim N)表示当前兴奋程度
建(N)个点(N+1sim 2N)为中转节点
建(N)个点(2N+1sim 3N)表示(N)款游戏
那么对于(1le i,jle N,i|j),连边((i,N+j)),表示兴奋程度(i)能玩有趣程度为(j)的游戏
对于(1le ile n),连边((N+w_i,2N+i),(2N+i,e_i)),表示第(i)款游戏有趣程度为(w_i),玩完后兴奋程度变为(e_i)
那么连边的复杂度就是喜闻乐见的调和级数:(sum_{i=1}^Nlimits frac Ni=O(Nlog N))
接着跑一遍Tarjan,DFS一边求出哪些强连通分量可以被初始点达到
然后判断一下就好了,注意大小为(1)的强连通分量不是环。
时间复杂度 (O(TNlog N))
空间复杂度 (O(Nlog 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
",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;
}