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年<软件学报>的<一个有效的多边形裁剪算法>(刘勇奎,高云,黄有群)这篇论文,所使用的理论与算法大都基于本文,对论文中部分阐述进行了详细解释,并提 ...
随机推荐
- Git:七、标签(tag)
1.创建标签:切换到需要打标签的分支 1)直接打在最新commit的版本上 git tag <tagname> 2)找到commit id git tag <tagname> ...
- per-CPU变量
为什么需要per-CPU变量 假设系统中有4个cpu, 同时有一个变量在各个CPU之间是共享的,每个cpu都有访问该变量的权限. 当cpu1在改变变量v的值的时候,cpu2也需要改变变量v的值.这时候 ...
- MFC拖拽、选择目录、遍历文件
1.选择目录 void CDecryptFileDlg::OnBnClickedSel() { std::wstring selectedDir; WCHAR szDir[MAX_PATH]; Zer ...
- Java多线程基础(二)
1.多线程数据安全 线程同步:多个线程需要访问同一资源时,需要以某种顺序来确定该资源某一时刻只能被一个线程使用.从而,解决并发操作可能带来的异常. 2.同步代码块实现同步(部分代码的访问,我们希望它同 ...
- 创建你的第一个Composer/Packagist包
今天我们要介绍一下如何通过Composer和Packagist向PHP社区贡献代码包.首先,如果你是一个PHP开发者但是还不知道什么是Composer,请先参考了一下这篇文章http://docs.p ...
- Python面试笔记三
1. 类继承 有如下的一段代码: python对象 如何调用类A的show方法了,方法如下: python对象 __class__方法指向了类对象,只用给他赋值类型A,然后调用方法show,但是用完了 ...
- mysql的分组
以下是根据老师的视屏写的总结,要自己实际操作以下. 首先老师一顿操作猛如虎,得到以下的表. 然后进行以下的操作: 发现筛选时报错了,老师的解释实说,分组是因为mysql不知道选择谁而出现报错,因为pa ...
- Flink Event Time Processing and Watermarks(文末有翻译)
If you are building a Realtime streaming application, Event Time processing is one of the features t ...
- 在dialog的content中嵌入select的获取选中值方法
var shortNameList = "<select><option value='1'>1</option><option value='2' ...
- 10分钟,AppCan帮你搞定跨平台开发APP问题!
跨平台开发APP时,开发者总会遇到一些问题,如打包失败等等,尤其对于iOS来说,由于它的限制性会导致一些状况发生(如证书上传问题等),小编总结了几个AppCan在线IOS打包失败常见的情况及排查技巧, ...