Processing math: 100%

luogu P3349 [ZJOI2016]小星星

题目链接

发现要求的一一映射的条件可以转化为,图中的每个点至少被树上的点映射了一次。考虑对此进行容斥,每次限定一个集合内的数不可被映射,最后乘上容斥系数。

f[x][i] 表示树上 x 被映射到 i 的方案数,则转移为:

f[x][i]=vsonjSmap[i][j]×f[v][j]

其中 S 表示没有被限制的集合, map 的图的邻接矩阵。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define int long long

using namespace std;

const int N = 20;
int n, m, head[N], cnt, map[N][N], f[N][N];
struct Edge
{
	int nxt, to;
}g[N * N];

void add(int from, int to)
{
	g[++cnt].nxt = head[from];
	g[cnt].to = to;
	head[from] = cnt;
}

void init()
{
	scanf("%lld %lld", &n, &m);
	for (int i = 1, x, y; i <= m; i++)
		scanf("%lld %lld", &x, &y), map[x][y] = map[y][x] = 1;
	for (int i = 1, x, y; i < n; i++)
		scanf("%lld %lld", &x, &y), add(x, y), add(y, x);
}

void calc(int x, int fa, int S)
{
	for (int i = 1; i <= n; i++)
		if ((S & (1 << i - 1)) == 0)
			f[x][i] = 1;
		else
			f[x][i] = 0;
	for (int i = head[x]; i; i = g[i].nxt)
	{
		int v = g[i].to;
		if (v == fa) continue;
		calc(v, x, S);
		for (int j = 1; j <= n; j++)
		{
			if ((S & (1 << j - 1))) continue;
			int tmp = 0;
			for (int k = 1; k <= n; k++)
			{
				if ((S & (1 << k - 1)) || !map[j][k]) continue;
				tmp += f[v][k];
			}
			f[x][j] *= tmp;
		}
	}
}

void work()
{
	int ans = 0, MaxN = 1 << n;
	for (int i = 0; i < MaxN; i++)
	{
		calc(1, -1, i);
		int sum = 0, tmp = 0;
		for (int j = 1; j <= n; j++)
			if ((i & (1 << j - 1))) tmp++;
			else sum += f[1][j];
		tmp = (tmp & 1) ? -1 : 1;
		ans = (ans + tmp * sum);
	}
	printf("%lld\n", ans);
}

signed main()
{
	init();
	work();
	return 0;
}

posted @   With_penguin  阅读(92)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
点击右上角即可分享
微信分享提示