专栏文章

数学 Trick 之:宽限+反演

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipkcprg
此快照首次捕获于
2025/12/03 13:25
3 个月前
此快照最后确认于
2025/12/03 13:25
3 个月前
查看原文

能够解决的问题

有不好刻画/转移的勾石限制的一些问题。

优缺点

优点:代码简单,且这一类基本上都是区分度高的题。
缺点:适用性不广泛。

思路

既然有勾石限制,那我们就宽限
  1. 我们可以尝试将限制变简单,然后就可以考虑能否反演。
注:此时一定不要想着这个新限制能否转移,否则大脑容易焦虑,退缩,只管推反演就行了。
  1. 当我们发现一个能反演的更简单的性质了,我们可以考虑转移,然后:
  • 如果 TLE,就再放宽。
  • 实在放不宽,果断放弃
思路比较抽象,我们通过一个具体例题来讲。

例题与代码

这道题中恶心的限制在于:
  • 必须与原图一一对应(不重不漏用每个点)。
那么我们就可以考虑放宽一点,既然单射太难,那么就多射,变为:
  • 原图每个点都唯一对应树上的点集 SS
此时,我们可以列出状态 dpu,Sdp_{u,S} 表示 uu 的子树中,可以重复的用点集 SS 的点对应。
我们发现这样不好转移,因为我们无法得知图中转移边(即父亲与儿子对应点间的边)的有无,所以我们加一维: dpu,j,Sdp_{u, j, S} 表示 uu 的子树中,uu 对应 jj,可以重复的用点集 SS 的点对应。
那么我们有(不要问我怎么推出来的,都这个程度了):
dpu,j,S=sonE(u)faukSdpson,k,S[(j,k)G]dp_{u, j, S} = \sum_{son \in\text{E}(u) \setminus fa_u}\sum_{k \in S}dp_{son, k, S}[(j, k) \in \text{G}]
时间复杂度 O(n32n)O(n^32^n),这题需要卡常,60pts60\text{pts} 就够了。
CPP
#include <bits/stdc++.h>
using namespace std;

#define int long long
constexpr int maxn = 18, maxk = 131072;

int n, m, a, b, dp[maxn][maxn][maxk], G[maxn][maxn], g[maxk], f[maxk], ans, K, sz[maxk];
vector <int> T[maxn];

int kth(int S, int k) {
	return ((S >> (k - 1)) & 1);
}
int siz(int S) {
	int ress = 0;
	for (int i = 1; i <= n; i++) {
		ress += kth(S, i);
	}
	return ress;
}

void dfs(int t, int g, int fa, int S) {
	if (dp[t][g][S]) return ;
	dp[t][g][S] = 1;
	int nowsum;
	for (int now : T[t]) {
		if (now == fa) continue;
		nowsum = 0;
		for (int i = 1; i <= n; i++) {
			if (kth(S, i) && G[g][i]) {
				dfs(now, i, t, S);
				nowsum += dp[now][i][S];
			}
		}
		dp[t][g][S] *= nowsum;
	}
	return ;
}

int p(int x) {
	return ((x & 1) ? -1 : 1);
}

signed main() {
	input.read(n, m);
	K = (1 << n);
	for (int S = 0; S < K; S++) {
		sz[S] = siz(S);	
	}
	for (int i = 1; i <= m; i++) {
		input.read(a, b);
		G[a][b] = G[b][a] = 1;
	}
	for (int i = 1; i < n; i++) {
		input.read(a, b);
		T[a].push_back(b);
		T[b].push_back(a);
	}
	for (int j = 1; j <= n; j++) {
		for (int S = 0; S < K; S++) {
			if (!kth(S, j)) continue;
			dfs(1, j, 0, S);
		}
	}
	
	for (int S = 0; S < K; S++) {
		for (int i = 1; i <= n; i++) {
			if (!kth(S, i)) continue;
			g[S] += dp[1][i][S];
		}
	}
	
	for (int S = 0; S < K; S++) {
		ans += p(n - sz[S]) * g[S];
	}
	
	output.write(ans, '\n');
	
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...