\(GZOI2017D1T2\)
题目链接?
做了半天一直不对,交一发就A了?样例出锅了吧。。
很显然对每个兴奋程度向能玩的游戏连边,游戏向相应的兴奋程度连边,然后问题转化为有多少游戏在一个环上,且能被兴奋程度为\(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;
}