【BZOJ 4455】【UOJ #185】【ZJOI 2016】小星星

时间:2024-01-08 10:00:56

http://www.lydsy.com/JudgeOnline/problem.php?id=4455

http://uoj.ac/problem/185

有一个$O(n^n)$的暴力,放宽限制可以转化成$O(2^n)$的容斥,容斥每一层统计用$O(n^3)$的dp来统计。时间复杂度$O(n^3 2^n)$。

卡常!存图用邻接表!减小非递归函数的使用,尽量写到主函数里!

最后终于卡过了QwQ【BZOJ 4455】【UOJ #185】【ZJOI 2016】小星星

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 20;
int in() {
int k = 0, fh = 1; char c = getchar();
for(; c < '0' || c > '9'; c = getchar())
if (c == '-') fh = -1;
for(; c >= '0' && c <= '9'; c = getchar())
k = (k << 3) + (k << 1) + c - '0';
return k * fh;
} ll f[N][N];
bool mp[N][N];
struct node {int nxt, to;} T[N << 1];
int pointT[N], cntT = 0, n, m, fa[N], use[N], usenum; void insT(int u, int v) {T[++cntT] = (node) {pointT[u], v}, pointT[u] = cntT;} void mkfa(int x) {
for(int i = pointT[x]; i; i = T[i].nxt)
if (T[i].to != fa[x]) {
fa[T[i].to] = x;
mkfa(T[i].to);
}
} ll cal(int x, int y) {
ll mul = 1, ret; int v;
for(int i = pointT[x]; i; i = T[i].nxt)
if ((v = T[i].to) != fa[x]) {
ret = 0;
for(int j = 1; j <= usenum; ++j)
if (mp[y][use[j]]) ret += f[v][use[j]];
mul *= ret;
}
return mul;
} void dfs(int x) {
for(int i = pointT[x]; i; i = T[i].nxt)
if (T[i].to != fa[x]) dfs(T[i].to); for(int i = 1; i <= usenum; ++i)
f[x][use[i]] = cal(x, use[i]);
} int main() {
n = in(); m = in();
int u, v;
for(int i = 1; i <= m; ++i) {
u = in(); v = in();
mp[u][v] = mp[v][u] = true;
}
for(int i = 1; i < n; ++i) {
u = in(); v = in();
insT(u, v); insT(v, u);
} fa[1] = 0; mkfa(1); int tot = (1 << n) - 1, mu = n & 1;
ll ret, ans = 0;
for(int i = 1; i <= tot; ++i) {
usenum = 0;
for(int j = 0; j < n; ++j)
if ((1 << j) & i) use[++usenum] = j + 1; dfs(1);
ret = 0;
for(int i = 1; i <= usenum; ++i)
ret += f[1][use[i]];
if ((usenum & 1) == mu) ans += ret;
else ans -= ret;
} printf("%lld\n", ans);
return 0;
}