解:首先,n<=20的直接暴力建图然后状压哈密顿回路,相信大家都会。固定1为起点,fi,s表示结尾为i点,状态为s。每次遍历i的出边转移,最后遍历1的出边统计答案。n22n

然后就是正经题解了。先考虑K = 1的时候。对于一个子树,我们发现它只有三个地方有出边,左右上。而除此之外内部怎么连是没关系的,只要满足每个点都经过就行了。

于是就设fi,j表示以i为根的子树中,与外界连通状态为j的方案数。0表示从左右出去且经过根节点,1表示从左上出去,2表示从右上出去,3表示从左右出去且不经过根节点(这是为了方便转移才设的)。

每次合并两个子树而非一次做整个根节点(方便之后K > 1的时候),于是我们考虑每个状态如何被转移来:

  • 0由所有子树中某两个相邻的12状态和两边的0状态转移来。也就是从0 + 0(前面有两个相邻的12)或1 + 2转移来。
  • 1由最后的一个1和前面的所有0转移过来。也就是3 + 1。
  • 2由最前面的一个2和后面的所有0转移过来,也就是2 + 0,注意要特判当前子树为第一个子树时的情况。
  • 3由所有0转移过来,也就是0 + 0。

于是我们得到了一个O(n)的树形DP,注意根节点最后一个子树合并上来的时候,状态0还有一种情况就是最左2 + 中间0 + 最右1也就是2 + 1的转移。

然后输出f[1][0]即可获得30分,配合暴力有50分。

 #include <bits/stdc++.h>

 const int N = , MO = ;

 struct Edge {
int nex, v;
}edge[N << ]; int tp; int e[N], n, K, fa[N], stk[N], top, pw[];
int f[][];
std::vector<int> G[N];
std::bitset<N> bt[N]; inline void add(int x, int y) {
tp++;
//printf("add %d %d \n", x, y);
bt[x].set(y);
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} void DFS(int x) {
if(!G[x].size()) {
stk[++top] = x;
}
for(int i = ; i < (int)G[x].size(); i++) {
int y = G[x][i];
DFS(y);
}
return;
} inline void link(int x, int y) {
if(bt[x][y]) {
return;
}
add(x, y);
add(y, x);
return;
} inline void out(int x) {
for(int i = ; i < n; i++) {
printf("%d", (x >> i) & );
}
return;
} namespace k1 {
int f[N][];
void DFS(int x) {
if(!G[x].size()) {
f[x][] = f[x][] = f[x][] = ;
//printf("x = %d %d %d %d %d \n", x, f[x][0], f[x][1], f[x][2], f[x][3]);
return;
}
f[x][] = ;
for(int i = ; i < G[x].size(); i++) {
int y = G[x][i];
DFS(y);
///merge
int t0 = (1ll * f[x][] * f[y][] % MO + 1ll * f[x][] * f[y][] % MO) % MO;
int t1 = 1ll * f[x][] * f[y][] % MO;
int t2 = i ? 1ll * f[x][] * f[y][] % MO : f[y][];
int t3 = 1ll * f[x][] * f[y][] % MO;
if(x == && i == G[x].size() - ) {
(t0 += 1ll * f[x][] * f[y][] % MO) %= MO;
}
f[x][] = t0;
f[x][] = t1;
f[x][] = t2;
f[x][] = t3;
}
//printf("x = %d %d %d %d %d \n", x, f[x][0], f[x][1], f[x][2], f[x][3]);
return;
}
inline void solve() {
DFS();
printf("%d\n", f[][]);
return;
}
} int main() { //freopen("polygon.in", "r", stdin);
//freopen("polygon.out", "w", stdout); scanf("%d%d", &n, &K); for(int i = , x; i <= n; i++) {
scanf("%d", &x);
add(x, i); add(i, x);
fa[i] = x;
G[x].push_back(i);
} for(int i = ; i <= n; i++) std::sort(G[i].begin(), G[i].end()); if(K == ) {
k1::solve();
return ;
} DFS(); for(int i = ; i <= top; i++) {
for(int j = ; j <= K; j++) {
int temp = i + j;
if(temp > top) {
temp %= top;
}
if(!temp) {
temp = top;
}
link(stk[i], stk[temp]);
}
} int lm = ( << n);
for(int i = ; i <= lm; i++) pw[i] = pw[i >> ] + ;
f[][] = ;
for(int s = ; s < lm; s++) {
for(int x = ; x <= n; x++) {
/// f[x][s]
if(!f[x][s]) continue;
//printf("f %d ", x); out(s); printf(" = %d \n", f[x][s]);
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if((s >> (y - )) & ) {
continue;
}
(f[y][s | ( << (y - ))] += f[x][s]) %= MO;
}
}
}
int ans = ;
for(int i = e[]; i; i = edge[i].nex) {
int y = edge[i].v;
ans = (ans + f[y][lm - ]) % MO;
}
printf("%lld\n", 1ll * ans * (MO + ) / % MO);
return ;
}

50分代码

接下来说说K > 1的部分:

LOJ#2723 多边形的更多相关文章

  1. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  2. LOJ 3056 「HNOI2019」多边形——模型转化+树形DP

    题目:https://loj.ac/problem/3056 只会写暴搜.用哈希记忆化之类的. #include<cstdio> #include<cstring> #incl ...

  3. 【loj - 3056】 「HNOI2019」多边形

    目录 description solution accepted code details description 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时 ...

  4. LOJ 2548 「JSOI2018」绝地反击 ——二分图匹配+网络流手动退流

    题目:https://loj.ac/problem/2548 如果知道正多边形的顶点,就是二分答案.二分图匹配.于是写了个暴力枚举多边形顶点的,还很愚蠢地把第一个顶点枚举到 2*pi ,其实只要 \( ...

  5. Loj 2008 小凸想跑步

    Loj 2008 小凸想跑步 \(S(P,p_0,p_1)<S(P,p_i,p_{i+1})\) 这个约束条件对于 \(P_x,P_y\) 是线性的,即将面积用向量叉积表示,暴力拆开,可得到 \ ...

  6. LOJ 一本通一句话题解系列:

    第一部分 基础算法 第 1 章 贪心算法 1):「一本通 1.1 例 1」活动安排:按照结束时间排序,然后扫一遍就可以了. 2):「一本通 1.1 例 2」种树:首先要尽量的往区间重叠的部分种树,先按 ...

  7. [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC

    [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC 试题描述 九条可怜是一个爱玩游戏的女孩子. 最近她在玩一个无双割草类的游戏,平面上有 \(n\) 个敌人,每一个敌人的坐标为 ...

  8. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  9. 任意多边形切割/裁剪(附C#代码实现)

    本实现主要参考了发表于2003年<软件学报>的<一个有效的多边形裁剪算法>(刘勇奎,高云,黄有群)这篇论文,所使用的理论与算法大都基于本文,对论文中部分阐述进行了详细解释,并提 ...

随机推荐

  1. webpack安装、基本配置

    文章结构: 什么是webpack? 安装webpack webpack基本配置 一.什么是webpack? 在学习react时发现大部分文章都是react和webpack结合使用的,所以在学react ...

  2. Windows应急响应常识

    Windows 应急响应 常见事件ID 1102 清理审计日志 4624 账号登陆成功 4625 账号登陆失败 4672 授予特殊权限 4720 创建用户 4726 删除用户 4728 将成员添加到启 ...

  3. [转]QQ空间、新浪微博、腾讯微博等一键分享API链接代码

    转自------ 1.新浪微博:http://service.weibo.com/share/share.php?url= count=表示是否显示当前页面被分享数量(1显示)(可选,允许为空)&am ...

  4. linux kernel内存碎片防治技术

    Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片正式buddy system的弱点之一,为了预防以及解决碎片问题,kernel采取了一些实用技术,这 ...

  5. python3.x pool.map方法的实质

    我使用多进程的一般方式,都是multiprocessing模块中的Pool.map()方法.下面写一个简单的示例和解析.至于此种方法使用多进程的效率问题,还希望大佬予以指正. 示例: "&q ...

  6. LeetCode算法题-N-ary Tree Postorder Traversal(Java实现)

    这是悦乐书的第269次更新,第283篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第136题(顺位题号是590).给定一个n-ary树,返回其节点值的后序遍历.例如,给定 ...

  7. Thirft简单使用

    安装Thrift 到thrift官网下载thrift.exe http://thrift.apache.org/download 将thrift-0.10.0.exe复制到C:\Program Fil ...

  8. 生成文件的MD5值

    import hashlib #########测试################# m = hashlib.md5() m.update(b"hello") m.update( ...

  9. 【English EMail】Compensation Planning Memo

    Data Foundation  数据基础 [faʊnˈdeʃən] Interesting newsletter for data foundation practice. Annual Code ...

  10. Starting sshd: /var/empty/sshd must be owned by root and not group or world-writable.

    Starting sshd: /var/empty/sshd must be owned by root and not group or world-writable.      [FAILED] ...