基环树复习

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基环树复习相关的知识,希望对你有一定的参考价值。

基环树复习

0.定义

基环树,又称环套树,有 n n n个点 n n n条边,比树多一条边。

n n n n n n​​边是一个连通图是拥有唯一环,如下图。

否则当图不连通但是每个连通块点数等于边数时为基环树森林


2.习题

CF711D. Directed Roads

给定 n n n n n n边,可改变边方向,有多少种无环方案。

找到所有环。

答案就是: 2 n − t o t × ∏ i = 1 c n t ( 2 s i z e i − 2 ) \\large 2^{n-tot}\\times \\prod\\limits_{i=1}^{cnt}(2^{size_i}-2) 2ntot×i=1cnt(2sizei2)

其中 t o t tot tot是所有环的长度之和, c n t cnt cnt是环的个数, s i z e i size_i sizei是第 i i i个环的长度。

2 s i z e i − 2 \\large 2^{size_i}-2 2sizei2​就是减去全部正向、反向两种成环的情况。

2 n − t o t 2^{n-tot} 2ntot 就是剩下非环边可以任意。

// Problem: CF711D Directed Roads
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF711D
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Date: 2021-03-06 17:15:25
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=2e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int n,h[N],cnt,vis[N];
int b[N],id,dep[N];	//dep 用来统计环的长度, b[]统计环的个数.
struct edge{
	int to,nt;
}e[N<<1];
void add(int u,int v){
	e[++cnt]={v,h[u]},h[u]=cnt;
	//e[++cnt]={u,h[v]},h[v]=cnt;
}
void dfs(int u,int d){		//求环的个数和长度.
	vis[u]=1,dep[u]=d;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(!vis[v]) dfs(v,d+1);
		else if(vis[v]==1) b[++id]=dep[u]-dep[v]+1;
	}vis[u]=2;
}
ll ksm(ll a,ll n,int m=mod){
	ll ans=1;
	while(n){
		if(n&1) ans=ans*a%m;
		a=a*a%m;
		n>>=1;
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	ll ans=1,sum=0;
	for(int i=1,j;i<=n;i++){
		scanf("%d",&j);
		add(i,j);
	}
	for(int i=1;i<=n;i++) if(!dep[i]) dfs(i,1);
	for(int i=1;i<=id;i++) sum+=b[i],ans=ans*(ksm(2,b[i])-2)%mod;
	ans=ans*ksm(2,n-sum)%mod;
	printf("%lld\\n",ans);
	return 0;
}

P1453 城市环路

连通基环树的最大点权独立集问题。

找到唯一环上任意两相邻两点 ( u , v ) (u,v) (u,v)​,然后断开 e d g e ( u , v ) edge(u,v) edge(u,v)这条边。

有三种情况:

  • 选择 u u u不选 v v v

  • 选择 v v v不选 u u u

  • 都不选

因此我们可以进行dp两次,每次以该结点为根结点进行 d p dp dp a n s = m a x ( d p ( u , 0 ) , d p ( v , 0 ) ) ans=max(dp(u,0),dp(v,0)) ans=max(dp(u,0),dp(v,0))

d p ( u , 0 ) dp(u,0) dp(u,0)表示不选择结点 u u u的答案, d p ( v , 0 ) dp(v,0) dp(v,0)表示不选择结点 v v v的最大答案。

这里 d p dp dp俩次就够了,因为上面两种情况已经包括了第三种情况。

// Problem: P1453 城市环路
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1453
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Date: 2021-03-08 16:11:55
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int h[N],cnt;
struct edge{
	int to,nt;
}e[N<<1];
void add(int u,int v){
	e[++cnt]={v,h[u]},h[u]=cnt;
	e[++cnt]={u,h[v]},h[v]=cnt;
}
int dp[N][2],rt,a[N];
void dfs(int u,int fa){		//树dp
	dp[u][0]=0,dp[u][1]=a[u];
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(v==fa||v==rt) continue;
		dfs(v,u);
		dp[u][1]+=dp[v][0];
		dp[u][0]+=max(dp[v][0],dp[v][1]);
	}
}
int vis[N],ok,ans;
void herio(int u,int fa){
	//printf("u=%d\\n",u);
	if(ok) return;//只用dfs两次
	vis[u]=1;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(v==fa) continue;
		if(!vis[v]) herio(v,u);
		else {
			ok=1;
			rt=u,dfs(u,v);	//不选u
			ans=max(ans,dp[u][0]);
			rt=v,dfs(v,u);	//不选v
			ans=max(ans,dp[v][0]);
		}
	}
}
int main(){
	int n;scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		int u,v;scanf("%d%d",&u,&v);++u,++v;
		add(u,v);
	}
	herio(1,0);
	double k;scanf("%lf",&k);
	printf("%.1f\\n",k*ans);
	return 0;
}

P2607 [ZJOI2008]骑士

基环树森林的最大权独立集

与上题类似,每个连通子图树dp,把多个连通子图的贡献加起来即可。

找环可维护一个 f a [ u ] fa[u] fa[u]数组。

// Problem: P2607 [ZJOI2008]骑士
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2607
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-03-08 17:24:11
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e6+5以上是关于基环树复习的主要内容,如果未能解决你的问题,请参考以下文章

基环树一统天下千秋万代

Hdu第八场 树形dp+基环树

bzoj1040(ZJOI2008)骑士——基环树

基环树

[HDU 5304] 基环树计数

[bzoj2878][Noi2012]迷失游乐园(基环树dp)