@description@

小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有 n 颗小星星,用 m 条彩色的细线串了起来,每条细线连着两颗小星星。

有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了 n-1 条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。

小 Y 找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。

小 Y 想知道有多少种可能的对应方式。只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。

输入格式

第一行包含两个正整数 n,m,表示原来的饰品中小星星的个数和细线的条数。

接下来 m 行,每行包含两个正整数 u,v,表示原来的饰品中小星星 u 和 v 通过细线连了起来。

这里的小星星从 1 开始标号。保证 u ≠ v,且每对小星星之间最多只有一条细线相连。 接下来 n-1 行,每行包含两个正整数 u,v,表示现在的饰品中小星星 u 和 v 通过细线连了起来。保证这些小星星通过细线可以串在一起。

输出格式

输出共一行,包含一个整数表示可能的对应方式的数量。

如果不存在可行的对应方式则输出 0。

样例输入

4 3

1 2

1 3

1 4

4 1

4 2

4 3

样例输出

6

数据范围与提示

对于所有的数据,n <= 17, m <= n*(n-1)/2。

@solution@

普通的做法人人都会:定义 dp[i][j][s] 表示以 i 为根的子树,i 对应 j,这棵子树内已经用了集合 s 的点。

然后一波枚举子集转移。然后就炸了。

我没试过 FWT 行不行,不过好像比下面的算法多个 n 的复杂度,所以估计过不了。

考虑抽象问题模型:我们要找的其实是一个满足 “树边的两个端点对应过去也存在边” 这一限制的置换。

问题在于,置换必须要满足每个元素恰好被对应一次,所以我们才要枚举子集啊之类。

但其实只有一条边相邻两个点才会产生限制。因此我们直接统计置换是很吃亏的。

我们可以把置换看成 n 个点都要被对应,通过容斥转成只有 m 个点可以被对应。

形式化来说,假如我们可被对应的点的集合为 S。

我们的原问题相当于 S 中每一个点都要被对应,通过容斥转为 T 中的点可以被对应,其中 \(T\subset S\)。

那么就回到我们一开始的树形 dp,只是少了 s 这一维,变成 dp[i][j] 表示以 i 为根的子树,i 对应 j 的方案数。

外层容斥 \(O(2^n)\),树形 dp 的复杂度为 \(O(n^3)\),所以总时间复杂度为 \(O(2^n*n^3)\)。

@accepted code@

#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
vector<int>G[20];
void addedge(int u, int v) {
G[u].push_back(v);
G[v].push_back(u);
}
bool tag[20];
int A[20][20], n, m;
ll dp[20][20];
void dfs(int x, int f) {
for(int i=0;i<G[x].size();i++)
if( G[x][i] != f ) dfs(G[x][i], x);
for(int i=0;i<n;i++)
if( tag[i] ) {
dp[x][i] = 1;
for(int j=0;j<G[x].size();j++) {
int p = G[x][j];
if( p == f ) continue;
ll del = 0;
for(int k=0;k<n;k++)
if( A[i][k] ) del += dp[p][k];
dp[x][i] *= del;
}
}
else dp[x][i] = 0;
}
int f[1<<20];
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=m;i++) {
int u, v; scanf("%d%d", &u, &v), u--, v--;
A[u][v] = A[v][u] = true;
}
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v), u--, v--;
addedge(u, v);
}
int t = (1<<n); f[0] = 1;
for(int i=1;i<t;i++)
f[i] = (i&1) ? -f[i>>1] : f[i>>1];
ll ans = 0;
for(int s=0;s<t;s++) {
for(int i=0;i<n;i++)
tag[i] = !((s>>i) & 1);
dfs(0, -1);
for(int i=0;i<n;i++)
if( tag[i] ) ans = ans + f[s]*dp[0][i];
}
printf("%lld\n", ans);
}

@details@

ZJOI 的题都是神仙题 * 2。

代码倒是非常简洁。

@loj - 2091@ 「ZJOI2016」小星星的更多相关文章

  1. 「LOJ2091」「ZJOI2016」小星星 容斥+DP

    题目描述 小 Y 是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有\(n\)颗小星星,用 \(m\)条彩色的细线串了起来,每条细线连着两颗小星星.有一天她发现,她的饰品被破坏了,很多细线都被拆掉 ...

  2. @loj - 2090@ 「ZJOI2016」旅行者

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Y 来到了一个新的城市旅行.她发现了这个城市的布局是网格状的 ...

  3. loj2091 「ZJOI2016」小星星

    ref 总的来说,就是 容斥转化为点对应到点集问题. 树形 dp 解决转化后的问题. #include <iostream> #include <cstring> #inclu ...

  4. 「ZJOI2016」小星星

    传送门 Description Solution 容斥,考虑有多少个节点不被匹配到,求出的方案,多个点可以同时不被匹配到 状态压缩+树形dp Code  #include<bits/stdc++ ...

  5. @loj - 2093@ 「ZJOI2016」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Yuuka 遇到了一个题目:有一个序列 a1,a2,..., ...

  6. @loj - 2092@ 「ZJOI2016」大森林

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Y 家里有一个大森林,里面有 n 棵树,编号从 1 到 n. ...

  7. 「ZJOI2016」解题报告

    「ZJOI2016」解题报告 我大浙的省选题真是超级神仙--这套已经算是比较可做的了. 「ZJOI2016」旅行者 神仙分治题. 对于一个矩形,每次我们从最长边切开,最短边不会超过 \(\sqrt{n ...

  8. Loj #3042. 「ZJOI2019」麻将

    Loj #3042. 「ZJOI2019」麻将 题目描述 九条可怜是一个热爱打麻将的女孩子.因此她出了一道和麻将相关的题目,希望这题不会让你对麻将的热爱消失殆尽. 今天,可怜想要打麻将,但是她的朋友们 ...

  9. 2090. 「ZJOI2016」旅行者 分治,最短路

    2090. 「ZJOI2016」旅行者 链接 loj 思路 \((l,mid)(mid+1,r)\).考虑跨过mid的贡献. 假设选的中间那条线的点为gzy,贡献为\(dis(x,gzy)+dis(g ...

随机推荐

  1. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

  2. OpenLayers使用symbolizers样式特征

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...

  3. Boost Asio教程集合

    http://zh.highscore.de/cpp/boost/ 第七章 https://mmoaay.gitbooks.io/boost-asio-cpp-network-programming- ...

  4. java图形验证码生成工具类及web页面校验验证码

    最近做验证码,参考网上案例,发现有不少问题,特意进行了修改和完善. 验证码生成器: import javax.imageio.ImageIO; import java.awt.*; import ja ...

  5. Hystrix线程池配置

    Hystrix配置文件配置 断路器: hystrix.command.default.circuitBreaker.requestVolumeThreshold(当在配置时间窗口内达到此数量的失败后, ...

  6. python 索引上的合并

  7. python 变量离散化

  8. NOIP模拟 9.09

    AK300分 果实计数 (count.pas/.c/.cpp) 时间限制:1s,空间限制32MB 题目描述: 淘淘家有棵奇怪的苹果树,这棵树共有n+1层,标号为0~n.这棵树第0层只有一个节点,为根节 ...

  9. cf round480D Perfect Groups

    题意:给一个序列,对于每一个连续的区间,区间内的数至少分成几个组,使得每个组内的数任意2个相乘是一个完全平方数(包括0). 输出每个组数的个数. $n \leq 5000 , |a_i| \leq 1 ...

  10. 蚁群算法MATLAB解TSP问题

    Excel表exp12_3_1.xls中数据为: clc clear all [xdata,textdata]=xlsread('exp12_3_1.xls'); %加载20个城市的数据,数据按照表格 ...