【图论】基环树

基环树其实并不是树,是指有n个点n条边的图,我们知道n个点n-1条边的连通图是树,再加一条边就会形成一个环,所以基环树中一定有一个环,长下面这样:

由基环树可以引申出基环内向树基环外向树

基环内向树如下,特点是每个点的出度为1

基环外向树如下,特点是每个点的入度为1

下面放点题,做到相关题目随时更新

基环树+组合数学

CF 1454E Number of Simple Paths

先记录环上的点,每个环上的点引出去的子树中,两点之间都只有一条路径,然后子树和其他点之间都有两条路径(因为有个环),可以循环计算每个子树,答案累加即可

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;

#define int long long

void solve()
{
	int n;
    cin >> n;
    vector<vector<int>> g(n + 1);
    vector<int> in(n + 1);
    for (int i = 0 ;i < n; i ++ )
    {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
        in[a] ++ , in[b] ++ ;
    }
    queue<int> q;
    for (int i = 1; i <= n; i ++ ) 
    {
        if (in[i] == 1) q.push(i);
    }
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < g[t].size(); i ++ )
        {
            int j = g[t][i];
            in[j] -- ;
            if (in[j] == 1) q.push(j);
        }
    }
    vector<int> huan;
    vector<int> st(n + 1);
    for (int i = 1; i <= n; i ++ )
    {
        if (in[i] > 1)
        {
            huan.push_back(i);
            st[i] = true;
        }
    }
    int ans = 0, sumtmp, sum = 0;
    vector<bool> visited(n + 1);
    function<void(int, int)> dfs = [&](int u, int fa)
    {
        sumtmp ++ ;
        if (visited[u]) return;
        visited[u] = true;
        for (int i = 0; i < g[u].size(); i ++ )
        {
            int j = g[u][i];
            if (j == fa || visited[j] || st[j]) continue;
            dfs(j, u);
        }
        return;
    };
    for (auto i : huan)
    {
        sumtmp = 0;
        dfs(i, -1);
        ans += sumtmp * (sumtmp - 1) / 2;
        ans += (sumtmp - 1) * (n - sumtmp - sum) * 2;
        sum += sumtmp - 1;
    }
    ans += huan.size() * (huan.size() - 1);
    cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	cin >> t;
	while (t -- )
	{
		solve();
	}
}

基环内向树+dfs

牛客 寒假集训1K 牛镇公务员考试

基环内向树(准确的说应该是森林)

编号 i 向 a[i] 连边,表示对其限制,我们可以发现环之外的链对答案没什么影响,因为确定了环上一点,可以倒推出链上的所有答案(原因就是约束关系),所以我们在环上任取一点,枚举这个点的五种答案,然后遍历一下环看这个答案是否合法

因为不保证联通,所以需要遍历每一个点

cpp 复制代码
#include <bits/stdc++.h>

using namespace std;

#define int long long
using i64 = long long;

typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<int, PII> PIII;

const int N = 1000010;
const int maxn = 1e6 + 1;
const int mod = 998244353;

void solve()
{
	int n;
	cin >> n;
	vector<int> a(n + 1);
	vector<string> s(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i] >> s[i];
	vector<bool> st(n + 1);
	int ans = 1;
	for (int i = 1; i <= n; i ++ )
	{
		if (st[i]) continue;
		int j = i;
		vector<int> huan;
		for (; !st[j]; j = a[j])
		{
			huan.push_back(j);
			st[j] = true;
		}
		auto iter = find(huan.begin(), huan.end(), j);
		if (iter == huan.end()) continue;
		huan = {iter, huan.end()};
		int tmp = 0;
		for (int k = 0; k < 5; k ++ )
		{
			int h = k;
			for (auto t : huan) h = (int)(s[t][h] - 'A');
			tmp += h == k;
		}
		ans = ans * tmp % mod;
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int t = 1;
	// cin >> t;
	while (t -- )
	{
		solve();
	}
}
相关推荐
罗湖老棍子42 分钟前
【模板】并查集(洛谷P3367)
算法·图论·并查集
wanderist.3 小时前
C++输入输出的一些问题
开发语言·c++·图论
Snow_day.1 天前
有关排列排列组合(1)
数据结构·算法·贪心算法·动态规划·图论
初晴や2 天前
【C++】图论:基础理论与实际应用深入解析
c++·算法·图论
闻缺陷则喜何志丹2 天前
【图论 DFS 换根法】3772. 子图的最大得分|2235
c++·算法·深度优先·力扣·图论·换根法
君义_noip4 天前
信息学奥赛一本通 2134:【25CSPS提高组】道路修复 | 洛谷 P14362 [CSP-S 2025] 道路修复
c++·算法·图论·信息学奥赛·csp-s
罗湖老棍子4 天前
信使(msner)(信息学奥赛一本通- P1376)四种做法
算法·图论·dijkstra·spfa·floyd·最短路算法
修炼地4 天前
代码随想录算法训练营第五十三天 | 卡码网97. 小明逛公园(Floyd 算法)、卡码网127. 骑士的攻击(A * 算法)、最短路算法总结、图论总结
c++·算法·图论
罗湖老棍子5 天前
【例4-6】香甜的黄油(信息学奥赛一本通- P1345)
算法·图论·dijkstra·floyd·最短路算法·bellman ford
--JR6 天前
015——图(1.图的相关概念与存储)
数据结构·c++·算法·链表·图论