P5676 [GZOI2017]小z玩游戏
CuSO4_and_5H2O · · 题解
思路
第一眼看到这个题目的时候感觉很简单,求强连通分量,然后看看有几个点的
-
- 是枚举
e_i 的倍数而不是w_i 的,这个还是比较好发现的,也不容易出错(是我菜了所以才会错这个点的)。
- 是枚举
-
- 枚举所有
e_i 的倍数的时候倍数乘以e_i 的数值限制一定是你定的N-1(这里的N是定的数组大小,如果你没定N就是你的比你数组的大小少一),这么说可能不太清楚,上代码。
- 枚举所有
for(int i=1; i<=n; i++) {
cin>>b[i],vec[a[i]].push_back(b[i]);
for(int j=2; !Map[b[i]] && j*b[i]<N; j++) //就是这里的<N了,如果是<=的话会错,因为他如果等于N的话到上边的tarjan代码的时候会访问vis[N]等数组,但是这些数组的范围是0到N-1的不包括N所以会出错
vec[b[i]].push_back(j*b[i]);
Map[b[i]]=114514;
}
-
- 我做的时候有一段时间对了六个点,空间超限了四个点,这是因为 vector 数组重复建了太多的边了导致空间爆掉,你肯能会问为什么会重复建边呢,下边个样例。
M(这里 M 指的是个很大的数)
#daa..adw##@!.ads%#.ads(这里泛指很多数字这行数字不重要)
1 1 1 1 1 1 1 ……1(有 M 个1)
这样的话要建
代码
这里就是代码了,题目并不难,看了思路之后自己打出来,代码也很好理解。
#include<bits/stdc++.h>
#define int long long
const int N=1e5+10;
using namespace std;
int n,a[N],b[N];
vector<int> vec[N];
int Map[N];
int jis,wuy[N],low[N],dfn[N],cnt,vis[N];
int ans;
stack<int> sta;
void tarjan(int x)
{
sta.push(x);vis[x]=1;
low[x]=dfn[x]=++cnt;
for(int i=0;i<vec[x].size();i++)
{
int nex=vec[x][i];
if(!dfn[nex]){
tarjan(nex);
low[x]=min(low[x],low[nex]);
} else if(vis[nex]) low[x]=min(low[x],dfn[nex]);
}
if(low[x]==dfn[x])
{
jis++;
while(1)
{
vis[sta.top()]=0;
wuy[sta.top()]=jis;
vis[sta.top()]=0;
if(sta.top()==x) break ;
sta.pop();
}
sta.pop();
}
}
signed main(){
int t;
cin>>t;
for(int qwq=1;qwq<=t;qwq++)
{
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(wuy, 0, sizeof wuy);
memset(vis, 0, sizeof vis);
memset(Map, 0, sizeof Map);
cnt=jis=ans=0;
for (int i = 0; i < N; ++i)
vec[i].clear();
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i];
for(int i=1;i<=n;i++)
{
cin>>b[i],vec[a[i]].push_back(b[i]);
for(int j=2;!Map[b[i]] && j*b[i]<N;j++)//把每个e_i枚举一遍
vec[b[i]].push_back(j*b[i]);
Map[b[i]]=114514;//标记
}
for(int i=1;i<=n;i++) if(!dfn[a[i]]) tarjan(a[i]);
for(int i=1;i<=n;i++) if(wuy[a[i]]==wuy[b[i]]) ans++;
cout<<ans<<endl;
}
}