有向图找环——Directed Roads ( Codeforces Round #369 (Div. 2) D )

  • 题目链接:
    http://www.codeforces.com/contest/711/problem/D

  • 分析:
    给出n个点,每个点的出度为1,然后给出每个点指向的那个点。求有多少种翻转的方法(选中哪些边进行一次反向)使得翻转后整个有向图中不存在环。范围最大为200000。

  • 题解:
    通过分析可得,整个图中有多少点就有多少边。我们只需要求出整个有向图中有多少个环,以及每一个环里有多少条边,然后就是一个组合问题:

    不在环里的边(假设有x条)的选择方法有: C(0x)+...C(xx)=2x

    每一个环里的边(假设有t条)的选择方法有: C(1t)+...C(t1t)=2t2

使用快速幂即可计算。

  • AC代码:
    这里写图片描述
/*************************************************************************
    > File Name: test.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 212345
#define MaxM MaxN*10
#define INF 0x3f3f3f3f
#define bug cout<<88888888<<endl;

const int mod  = 1000000007;
int n; 
int A[MaxN];
map<int, int> M; 

int huan[MaxN];

void init()
{
    CLR(huan);
    M.clear();
}


LL multi(LL a, LL b)
{
    LL ret = 1;   
    while(b>0)  
    {      
        if(b&1)
            ret=(ret*a)%mod;     
        a=(a*a)%mod;      
        b>>=1;   
    }   
    return ret;

}

int vis[MaxN];

void Find(int x,int num)
{
    vis[x]  = num;                   //记录从初始点出发走到该点要多少条边
    int y = A[x];                    //找到x指向的点y
    if(M[y] && M[y]!=M[x]) return ;  //如果y点被访问过并且是在以前查找中被访问过则退出
    if(M[y] == M[x])                 //如果y点被访问过并且是在当前查找中被访问过则记录为环
    {
        huan[++huan[0]] = num+1 - vis[y];
        return;
    }
    else                             //如果y点没有被访问过,则继续访问
    {
        M[y] = M[x];
        Find(y, num+1);              //走过的边数+1。
    }
}


int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d", &A[i]);
        }
        for(int i=1;i<=n;i++)
        {
            if(M[i]) continue;
            M[i] = i;
            Find(i,0);   
        }
        //计算答案
        LL ans = 0;
        LL cnt = 0;
        LL b = 1;
        for(int i=1;i<=huan[0];i++)
        {
            cnt += huan[i]; 

            b *= (( multi(2, huan[i]) - 2) % mod);
            b%=mod;

        }

        LL a  = multi(2, n - cnt);
        ans = a * b % mod;
        cout << ans << endl;

    }

    //system("pause");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值