LOJ#2723 多边形

解:首先,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 多边形的更多相关文章
- Loj #3056. 「HNOI2019」多边形
Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...
- LOJ 3056 「HNOI2019」多边形——模型转化+树形DP
题目:https://loj.ac/problem/3056 只会写暴搜.用哈希记忆化之类的. #include<cstdio> #include<cstring> #incl ...
- 【loj - 3056】 「HNOI2019」多边形
目录 description solution accepted code details description 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时 ...
- LOJ 2548 「JSOI2018」绝地反击 ——二分图匹配+网络流手动退流
题目:https://loj.ac/problem/2548 如果知道正多边形的顶点,就是二分答案.二分图匹配.于是写了个暴力枚举多边形顶点的,还很愚蠢地把第一个顶点枚举到 2*pi ,其实只要 \( ...
- Loj 2008 小凸想跑步
Loj 2008 小凸想跑步 \(S(P,p_0,p_1)<S(P,p_i,p_{i+1})\) 这个约束条件对于 \(P_x,P_y\) 是线性的,即将面积用向量叉积表示,暴力拆开,可得到 \ ...
- LOJ 一本通一句话题解系列:
第一部分 基础算法 第 1 章 贪心算法 1):「一本通 1.1 例 1」活动安排:按照结束时间排序,然后扫一遍就可以了. 2):「一本通 1.1 例 2」种树:首先要尽量的往区间重叠的部分种树,先按 ...
- [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC
[LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC 试题描述 九条可怜是一个爱玩游戏的女孩子. 最近她在玩一个无双割草类的游戏,平面上有 \(n\) 个敌人,每一个敌人的坐标为 ...
- canvas快速绘制圆形、三角形、矩形、多边形
想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...
- 任意多边形切割/裁剪(附C#代码实现)
本实现主要参考了发表于2003年<软件学报>的<一个有效的多边形裁剪算法>(刘勇奎,高云,黄有群)这篇论文,所使用的理论与算法大都基于本文,对论文中部分阐述进行了详细解释,并提 ...
随机推荐
- shell脚本批量ssh登陆主机并执行命令
shell脚本批量ssh登陆主机并执行命令 今天在客户现场遇到了这个问题,客户没有管理工具,无法批量登陆主机下发命令,几个个C段啊,让我一个一个登陆,.................. 所以写了个s ...
- MySQL分数排名同分并列与不并列查询
Scores表 | Id | Score | | 3.50 | | 3.65 | | 4.00 | | 3.85 | | 4.00 | | 3.65 | 并列 | Score | Rank | | | ...
- 如何利用MongoDB打造TOP榜小程序
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 今天我分享的主题内容大概是两部分,最主要的还是小游戏和小程序,第一部分就是跟大家分 ...
- Linux学习历程——Centos 7 uptime 、free命令
一.命令介绍 uptime命令 uptime命令用于查看系统负载信息以及系统运行时间等. free命令 free命令用于查看当前系统中内存使用量信息. 二.实例 uptime命令实例 直接运行 upt ...
- Linux AIDE(文件完整性检测)
一.AIDE的概念 AIDE:Advanced Intrusion Detection Environment,是一款入侵检测工具,主要用途是检查文档的完整性.AIDE在本地构造了一个基准的数据库,一 ...
- ibm z14大型主机介绍
IBM z14™大型主机 (z14)被设计为数字经济中值得信任的基础架构.它提供 特性和功能以满足对于新服务和更佳客户体验的需求,同时保护日益 增长的数据量,并遵从日益复杂的法规.IBM z14 ...
- 周末班:Python基础之面向对象基础
面向对象基础 面向对象和面向过程 编程思想是什么,就是用代码解决现实生活中问题的思路. 面向过程 核心点在过程二字,过程指的是解决问题的步骤,说白了就是先做什么再干什么.这种解决问题的思路就好比是工厂 ...
- logback.xml的使用,将日志异步保存到数据库中
想要把日志异步保存到数据库中,首先需要创建一个数据库,然后创建三张固定的表: https://github.com/xiaorenwu-dashijie/logback.git <?xml ve ...
- 3-STM32带你入坑系列(自己封装点亮一个灯的库--Keil)
2-STM32带你入坑系列(点亮一个灯--Keil) 首先建一个stm32f103x.h的文件,然后 #include "stm32f103x.h" 还记得上一节 现在呢就是做一个 ...
- Git命令集
安装 Window https://gitforwindows.org/ MAC http://sourceforge.net/projects/git-osx-installer/ git conf ...