zoukankan      html  css  js  c++  java
  • 基环树学习笔记 & CF711D Directed Roads

    传送门


    基环树

    定义:

    n个节点n条边的连通图。

    性质:

    很显然有且仅有一个环。

    如何找环:

    • dfs,每次读到一个节点入栈(类似Tarjan)
    • 拓扑排序:最后剩下的度数不为零的点一定就在环上

    题目通常解法:

    • 把环看做一个整个树的根,然后把子树的信息挂到环的每一个点上,问题变成了环上操作,再根据题目需求进行断环成链等操作。
    • 把环任意断开一条边,整张图就变成了一棵树,这时候可以方便地进行树形dp等。注意这时要特判断边的两端点的关系。

    解题思路

    首先这个题保证了每个点都有边,而且n个点n条边,我们推出这是个基环森林。

    然后我们发现环之外的边是可以任意方向的,而每个环上的点只要不是一个方向即可。

    所以容斥一下,答案为:

    [2^{n-sum{len}} prod (2^{len}-2) ]

    其中 len 为每个环的长度。

    AC代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<map>
    #include<vector>
    using namespace std;
    const int maxn=2e5+5;
    const long long mod=1e9+7;
    int n,p[maxn],in[maxn],x,cnt;
    long long k,num,ans=1;
    long long ksm(long long a,long long b){
    	if(b==0) return 1;
    	if(b==1) return a;
    	long long res=ksm(a,b/2);
    	if(b&1) return res*res%mod*a%mod;
    	return res*res%mod;
    }
    struct node{
    	int v,next;
    }e[maxn*2];
    void insert(int u,int v){
    	cnt++;
    	e[cnt].v=v;
    	e[cnt].next=p[u];
    	p[u]=cnt;	
    }
    int dfs(int u){
    	int cnt=1;
    	in[u]--;
    	for(int i=p[u];i!=-1;i=e[i].next){
    		int v=e[i].v;
    		if(in[v]==0||in[v]==1) continue;
    		cnt+=dfs(v);
    	}
    	return cnt;
    }
    void topo(){
    	queue<int> q;
    	for(int i=1;i<=n;i++){
    		if(in[i]==1){
    			q.push(i);
    			in[i]--;
    		}
    	}
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		for(int i=p[u];i!=-1;i=e[i].next){
    			int v=e[i].v;
    			if(v==u) continue;
    			in[v]--;
    			if(in[v]==1) q.push(v);
    		}
    	}
    }
    int main(){
    	ios::sync_with_stdio(false);
    	memset(p,-1,sizeof(p));
    	cin>>n;
    	for(int i=1;i<=n;i++){
    		cin>>x;
    		insert(i,x);
    		insert(x,i);
    		in[x]++;
    		in[i]++;
    	}
    	topo();
    	for(int i=1;i<=n;i++){
    		if(in[i]>=2){
    			k=dfs(i);
    			num+=k;
    			ans=(ans*(ksm(2,k)-2)%mod);
    		}
    	}
    	cout<<(ksm(2,n-num)*ans)%mod;
    	return 0;
    }
    
  • 相关阅读:
    AVFoundation 文本语音播报
    单元测试 + UI测试
    scrollView
    设备旋转---横竖屏切换
    SDK 开发 .a .framework .bundle (xcode引用) 依赖sdk工程
    多线程 NSThread 的使用
    多线程 NSOpeartion 的使用
    多线程 GCD 的使用
    swift pod 第三方库异常的处理
    按钮重复点击问题 UIbutton
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15459893.html
Copyright © 2011-2022 走看看