题目链接:cf 711D
这个题主要是读题意比较难
因为是n个点,n条边,那么肯定会有环存在
那么,一旦出现了环,就出现了题中给的非法情况
那么,我们根据连通情况将图中的点分类(按照乘法原理,先各自计算当前的集合之中有几个数,然后相乘)
在每个集合中,如果出现了环,假设环中的点数为x,那么可以的方案数目为(2^x-2)(样例1)
如果既有环,又有链子,假设环中的点数为x,链子中的点数为y,那么可以的方案数目为(2^x-2)*(2^y),当然在操作中是只能求出x+y的(样例3)
那么,这个题就是dfs搜索搞定的
cf的题目很多都是需要数学方法先想明白,其实代码实现确实不难吧
#include<bits/stdc++.h>
using namespace std;
#define LL __int64
const int maxn=2e5+50;
struct Edge{
int nxt,to;
}edge[maxn<<1];
LL mod=1e9+7,d[maxn],ans,sum,f[maxn];
int Head[maxn],cnt;
bool vis[maxn];
void addedge(int u,int v){
edge[cnt].nxt=Head[u];
edge[cnt].to=v;
Head[u]=cnt++;
}
void dfs(int u,int p,int dep){
if (!ans&&vis[u]){
ans=dep-d[u]?dep-d[u]:2;
return;
}
else if (vis[u]) return;
d[u]=dep;
vis[u]=true;
++sum;
for(int i=Head[u];~i;i=edge[i].nxt){
int v=edge[i].to;
if (v==p) continue;
dfs(v,u,dep+1);
}
}
int main(){
//freopen("input.txt","r",stdin);
f[0]=1;
for(int i=1;i<maxn;i++) f[i]=f[i-1]*2LL%mod;
int n,u;
while(scanf("%d",&n)!=EOF){
memset(Head,-1,sizeof(Head));
memset(vis,0,sizeof(vis));
cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&u);
addedge(u,i);
addedge(i,u);
}
LL res=1;
for(int i=1;i<=n;i++)
if (!vis[i]){
ans=sum=0;
dfs(i,-1,1);
res=((res*(f[ans]-2)%mod+mod)*f[sum-ans])%mod;
}
cout<<res<<endl;
}
return 0;
}