解:首先,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. ARouter学习随笔

    今天看了会ARouter,在这里简单记录下 跟着其他大哥的博客学习了下,总感觉不牢固,借此机会再次简单记录下. 第一步:ARouter 配置 android { defaultConfig { ... ...

  2. 牛客网:java入门实现遍历目录

    项目介绍 遍历目录是操作文件时的一个常见需求.比如写一个程序,需要找到并处理指定目录下的所有JS文件时,就需要遍历整个目录.该项目教会你如何使用流式编程和lambda表达式,帮助你进一步熟悉java8 ...

  3. CTS问题分析6

    遇到一个Android P相关的问题,和原来CTS/GTS 问题分析1的表现是一样的,但是将 这个修复cp过来,发现不生效,仍然报错,因此记录一下 问题初探 测试命令: run gts -m GtsG ...

  4. MongoDB在已有账号的实例下还原数据库报错的分析(error applying oplog)

    一. 背景 今天在MongoDB 4.0.4版本下,在还原恢复数据库时报错. 主要错误为: Failed: restore error: error applying oplog: applyOps: ...

  5. Redis学习笔记(4)——Redis五大数据结构介绍以及应用场景

    出处:https://www.jianshu.com/p/f09480c05e42 Redis是典型的Key-Value类型数据库,Key为字符类型,Value的类型常用的为五种类型:String.H ...

  6. 文本分类实战(八)—— Transformer模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  7. 贷款资讯类APP、贷款资讯网站廉价卖,需要的进来看看

    [app介绍]卡贷资讯app为您提供信用卡申请攻略及借款资讯以及贷款口子,让你借钱借款路上不再愁.[功能特点]1.资讯:聚合各种贷款资讯知识,掌握核心信用卡申请攻略,借款借钱不亏,亦不被骗:2.工具: ...

  8. WiFi广告强推的基本技术原理和一些相关问题

    WiFi推原理(转) 本文地址:http://jb.tongxinmao.com/Article/Detail/id/412 WiFi广告强推的基本技术原理和一些相关问题 WiFi广告推送原理就是利用 ...

  9. java如何获取一个对象的大小【转】

    When---什么时候需要知道对象的内存大小 在内存足够用的情况下我们是不需要考虑java中一个对象所占内存大小的.但当一个系统的内存有限,或者某块程序代码允许使用的内存大小有限制,又或者设计一个缓存 ...

  10. hotspot目录结构

    Hotspot的目录结构 ├─agent Serviceability Agent的客户端实现 ├─make 用来build出HotSpot的各种配置文件 ├─src HotSpot VM的源代码 │ ...