【loj - 3056】 「HNOI2019」多边形
description
小 R 与小 W 在玩游戏。
他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3,\dots,n\)。最开始凸多边形中有 \(n\) 条线段,即多边形的 \(n\) 条边。这里我们用一个有序数对 \((a,b)\)(其中 \(a<b\))来表示一条端点分别为顶点 \(a,b\) 的线段。
在游戏开始之前,小 W 会进行一些操作。每次操作时,他会选中多边形的两个互异顶点,给它们之间连一条线段,并且所连的线段不会与已存的线段重合、相交(只拥有一个公共端点不算作相交)。他会不断重复这个过程,直到无法继续连线,这样得到了状态 \(S_0\)。\(S_0\) 包含的线段为凸多边形的边与小 W 连上的线段,容易发现这些线段将多边形划分为一个个三角形区域。对于其中任意一个三角形,其三个顶点为 \(i,j,k(i<j<k)\),我们可以给这个三角形一个标号 \(j\),这样一来每个三角形都被标上了 \(2,3,\dots,n-1\) 中的一个,且没有标号相同的两个三角形。
小 W 定义了一种「旋转」操作:对于当前状态,选定 \(4\) 个顶点 \(a,b,c,d\),使其满足 \(1\leq a<b<c<d\leq n\) 且它们两两之间共有 \(5\) 条线段—— \((a,b),(b,c),(c,d),(a,d),(a,c)\),然后删去线段 \((a, c)\),并连上线段 \((b,d)\)。那么用有序数对 \((a,c)\) 即可唯一表示该次「旋转」。我们称这次旋转为 \((a,c)\) 「旋转」。显然每次进行完“旋转”操作后多边形中依然不存在相交的线段。
当小 W 将一个状态作为游戏初始状态展示给小 R 后,游戏开始。游戏过程中,小 R 每次可以对当前的状态进行「旋转」。在进行有限次「旋转」之后,小 R 一定会得到一个状态,此时无法继续进行「旋转」操作,游戏结束。那么将每一次「旋转」所对应的有序数对按操作顺序写下,得到的序列即为该轮游戏的操作方案。
为了加大难度,小 W 以 \(S_0\) 为基础,产生了 \(m\) 个新状态。其中第 \(i\) 个状态 \(S_i\) 为对 \(S_0\) 进行一次「旋转」操作后得到的状态。你需要帮助小 R 求出分别以 \(S_0,S_1,\dots,S_m\) 作为游戏初始状态时,小 R 完成游戏所用的最少「旋转」次数,并根据小 W 的心情,有时还需求出旋转次数最少的不同操作方案数。由于方案数可能很大,输出时请对 \(10^9+7\) 取模。
solution
把三角剖分每条边 \((a, b)\) 看成区间 \((a, b)\)(不妨假设 \(a < b\))。
则根据三角剖分的性质,任意两区间要么相离,要么包含,且存在区间 \((1,2),(2,3)\dots,(n-1,n)\) 与 \((1,n)\)。
考虑建树:区间 \((l, r)\) 的儿子为它所包含的极大区间 \((p, q)\)(不存在区间介于 \((p, q)\) 与 \((l, r)\) 之间)。
那么该树叶子为 \((1,2),(2,3)\dots,(n-1,n)\),根为 \((1,n)\)。
考虑这棵树的特殊性质:不存在儿子个数恰好为 \(1\) 的非叶结点,非叶结点数量为 \(n - 2\)(算上根结点),叶子数量为 \(n - 1\)。
那么显然这棵树是二叉树,且非叶结点的儿子个数恰为 2。
考虑旋转操作对应到树上的含义:右旋非叶结点(真就“旋转”操作啊)。
考虑终止状态的含义:非叶结点形成一条右链。也就是说不存在非叶结点 x 在 y 的左子树内。
我们可以先删掉叶结点。
每次旋转最多会使一个点不在某点左子树中,且总可以旋转最右链上的点使得左子树点减少。
那么最小次数即最右链上的点的左子树大小总和 cnt。
考虑方案数,每次可以任选最右链上某一点进行旋转。通过简单的组合推导可得方案数为:
\]
一次右旋只会影响 \(O(1)\) 个点,简单分类回答询问即可。
accepted code
#include <map>
#include <cstdio>
#include <vector>
#include <cassert>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
#define pr make_pair
#define last(v) v[v.size() - 1]
#define all(v) v.begin(), v.end()
#define pb(x) push_back(x)
const int MOD = int(1E9) + 7;
const int MAXN = 100000;
inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}
int fct[MAXN + 5], inv[MAXN + 5];
void init() {
fct[0] = 1; for(int i=1;i<=MAXN;i++) fct[i] = mul(fct[i - 1], i);
inv[1] = 1; for(int i=2;i<=MAXN;i++) inv[i] = sub(0, mul(inv[MOD % i], MOD / i));
}
map<pii, int>mp; int ncnt;
int id(int l, int r) {
if( mp.count(pr(l, r)) ) return mp[pr(l, r)];
else return mp[pr(l, r)] = (++ncnt);
}
vector<int>vl[MAXN + 5], vr[MAXN + 5];
bool rgt[MAXN + 5]; int siz[MAXN + 5], ch[2][MAXN + 5], fa[MAXN + 5];
int build(int l, int r, bool f) {
if( l + 1 == r ) return 0;
int x = id(l, r), m = last(vr[l]);
assert(last(vl[r]) == m);
vr[l].pop_back(), vl[m].pop_back(), fa[ch[0][x] = build(l, m, false)] = x;
vr[m].pop_back(), vl[r].pop_back(), fa[ch[1][x] = build(m, r, f)] = x;
siz[x] = siz[ch[0][x]] + siz[ch[1][x]] + 1, rgt[x] = f;
return x;
}
bool cmp(const int &x, const int &y) {return x > y;}
int main() {
init(); int W, n, m; scanf("%d%d", &W, &n);
for(int i=1;i<n;i++) vr[i].pb(i + 1);
for(int i=n;i>1;i--) vl[i].pb(i - 1);
for(int i=1;i<=n-3;i++) {
int x, y; scanf("%d%d", &x, &y);
vl[x].pb(y), vr[x].pb(y), vl[y].pb(x), vr[y].pb(x);
}
for(int i=1;i<=n;i++)
sort(all(vr[i])), sort(all(vl[i]), cmp);
build(1, n, true);
int ans = 1, cnt = 0;
for(int i=1;i<=ncnt;i++)
if( !rgt[i] ) cnt++, ans = mul(ans, inv[siz[i]]);
if( W == 1 ) printf("%d %d\n", cnt, mul(ans, fct[cnt]));
else printf("%d\n", cnt);
scanf("%d", &m);
for(int i=1;i<=m;i++) {
int l, r; scanf("%d%d", &l, &r);
int x = id(l, r), cnt1, ans1;
if( rgt[fa[x]] )
cnt1 = cnt - 1, ans1 = mul(mul(ans, fct[cnt - 1]), siz[x]);
else {
int p = ch[1][fa[x]], q = ch[1][x];
cnt1 = cnt, ans1 = mul(mul(ans, fct[cnt]), mul(siz[x], inv[siz[p] + siz[q] + 1]));
}
if( W == 1 ) printf("%d %d\n", cnt1, ans1);
else printf("%d\n", cnt1);
}
}
details
当然也可以不用像这样显性地建树。
事实上这道题转化问题的方法比较多,这里只是采用了我使用的一种。
最后代码实现比较简单,对问题一环套一环的分析更有趣些。
【loj - 3056】 「HNOI2019」多边形的更多相关文章
- 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 #3059. 「HNOI2019」序列
Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...
- Loj #3055. 「HNOI2019」JOJO
Loj #3055. 「HNOI2019」JOJO JOJO 的奇幻冒险是一部非常火的漫画.漫画中的男主角经常喜欢连续喊很多的「欧拉」或者「木大」. 为了防止字太多挡住漫画内容,现在打算在新的漫画中用 ...
- Loj 3058. 「HNOI2019」白兔之舞
Loj 3058. 「HNOI2019」白兔之舞 题目描述 有一张顶点数为 \((L+1)\times n\) 的有向图.这张图的每个顶点由一个二元组 \((u,v)\) 表示 \((0\le u\l ...
- Loj #3057. 「HNOI2019」校园旅行
Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...
- LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分
题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...
- LOJ 3057 「HNOI2019」校园旅行——BFS+图等价转化
题目:https://loj.ac/problem/3057 想令 b[ i ][ j ] 表示两点是否可行,从可行的点对扩展.但不知道顺序,所以写了卡时间做数次 m2 迭代的算法,就是每次遍历所有不 ...
- LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树
题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...
随机推荐
- 都说变量有七八种,到底谁是 Java 的亲儿子
网上罗列了很多关于变量的理解,良莠不齐,不知道哪些是对的,哪些是错的,所以笔者就这些博客和自己的理解写出这篇文章,如果有不对的地方,希望读者能够指正,感谢. 变量是我们经常用到的一种,我在刚学 Jav ...
- SpringBoot瘦身
1.介绍 本教程中,我们将研究如何使用spring-boot-thin-launcher项目来将Spring Boot项目瘦身. Spring Boot出了名的把所有依赖打包成单个可执行的Fat JA ...
- Centos慢慢长大(一)
1.写在前面 这将是一个系列性的文章.可能更多的是记录我在学习的过程中的一些感悟吧.我想强调的是在这一系列文章里我会从最小化的安装开始,然后逐渐的增加需要安装的软件.就象一个婴儿的诞生,慢慢的学走路. ...
- Verilog代码和FPGA硬件的映射关系(四)
其实在FPGA的开发中理想情况下FPGA之间的数据要通过寄存器输入.输出,这样才能使得延时最小,从而更容易满足建立时间要求.我们在FPGA内部硬件结构中得知,IOB内是有寄存器的,且IOB内的寄存器比 ...
- MySQL高可用篇之MHA集群
1 准备工作 1.1 修改主机名 vim /etc/hosts # 添加对应主机 192.168.28.128 mha1 192.168.28.131 mha2 192.168.28.132 mha3 ...
- Unity实现精灵资源动态加载
private Sprite LoadSourceSprite(string relativePath) { //把资源加载到内存中 UnityEngine.Objec ...
- word修改页眉使本页的页眉与别的页不一样
关键回答:双击要修改的页的页眉,word顶端的工具栏“页面设置”当中的“链接到前一个”选项不要选中,即可修改本页页眉使之与别的页不一样. 详见:https://iask.sina.com.cn/b/2 ...
- spring boot 入口源码分析
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); / ...
- Linux 比较常用的命令
#磁盘空间 df -h 显示已经挂载的分区列表 du -sh [file] 估算当前使用磁盘空间 du -sk * | sort -rn 以容量大小递减排序 文件搜索 find find [file_ ...
- AVIRIS反射率数据简介
Surface Reflectance 高光谱图像 ↑ AVIRIS高光谱成像光谱仪采集得到的原始图像为辐亮度图像,经过校正后的L1级产品为地表辐亮度信息.但是许多时候,我们更希望知道地面目标物的反射 ...