HNOI2019 多边形 polygon
HNOI2019 多边形 polygon
https://www.luogu.org/problemnew/show/P5288
这题镪啊。。。
首先堆结论:
显然终止状态一定是所有边都连向n了
根据样例及打表猜个结论,每一步一定可以新连一条到n的边,这个结论也很好证
然后可以把多边形分成若干区间,这些区间形成一棵树。具体划分方法很简单,就是用一些现有的点和中间所有边构成的多边形缩成一个区间,这些点要满足:编号连续,和只有编号最小最大的点与n有连边。比如样例中[1,3],[3,5],[1,5]。
左右端点对应编号最小最大的点,然后这些区间可以根据包含关系连成一棵树,而且这棵树除了根都是二叉的。代码可以模拟
然后每一步会新连一条到n的边,对应到区间上,会把当前区间分开成固定的,互不干扰的两个区间。
发现这就是个裸的dp是吧,\(f[l][r]\)表示这个区间的方案数,现在球出了这个dp,第1问就解决了
开始做第二问。第二问做了一个变换,假设是对\(l,t,k,r\)四个点进行\(l,k\)变换(膜lk),而且k!=n,那么由上面知道第一问答案不会变,而且这棵树的局部会这样变化:
变成
分析一波,下面三个叶子都没变,所以变的只是中间乘的组合数。那么照着这个树,爆算一波,答案是原答案乘\((C^{r-t-2}_{k-t-1}C^{r-l-2}_{t-l-1})\cdot (C^{k-l-2}_{t-l-1}C^{r-l-2}_{r-k-1})^{-1}\)
注意一个特殊情况,就是变换的k=n时,第一问答案-1,第二问答案是原答案乘\((C^{ans1}_{k-l-1})^{-1}C^{ans1-1}_{k-l-2}\),可以看成是1号点最后合并的时候最后合并这个点并且少合并1(ans1是原问题的第一问的答案)
#include<bits/stdc++.h>
#define il inline
#define rg register
#define vd void
#define mod 1000000007
il int gi(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f^=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,x[100010],y[100010];
int s[100010],m;
std::vector<int>G[100010];
int L[100010],R[100010],ch[100010][2],cnt;
std::vector<int>ch0;
il int build(int l,int r){
if(r-l<2)return 0;
++cnt;L[cnt]=l,R[cnt]=r;
int ret=cnt,t=*--std::lower_bound(G[l].begin(),G[l].end(),r);
ch[ret][0]=build(l,t);
ch[ret][1]=build(t,r);
return ret;
}
int p[100010],ip[100010];
il int C(int n,int m){
if(n<m)return 0;
return 1ll*p[n]*ip[m]%mod*ip[n-m]%mod;
}
il int invC(int n,int m){
if(n<m)return 0;
return 1ll*ip[n]*p[m]%mod*p[n-m]%mod;
}
int f[100010];
il vd dp(int x){
if(!ch[x][0]&&!ch[x][1]){f[x]=1;return;}
if(!ch[x][0]||!ch[x][1]){dp(ch[x][0]|ch[x][1]);f[x]=f[ch[x][0]|ch[x][1]];return;}
dp(ch[x][0]),dp(ch[x][1]);
f[x]=1ll*f[ch[x][0]]*f[ch[x][1]]%mod*C(R[x]-L[x]-2,R[ch[x][0]]-L[ch[x][0]]-1)%mod;
}
int main(){
// freopen("polygon.in","r",stdin);
// freopen("polygon.out","w",stdout);
int W=gi();
n=gi();
int ans1=n-3;
for(int i=1;i<n;++i)G[i].push_back(i+1);G[1].push_back(n);
for(int i=1;i<=n-3;++i){
x[i]=gi(),y[i]=gi();
if(x[i]>y[i])std::swap(x[i],y[i]);
if(y[i]==n)--ans1,s[++m]=x[i];
G[x[i]].push_back(y[i]);
}
for(int i=1;i<=n;++i)std::sort(G[i].begin(),G[i].end());
int Q=gi();
s[++m]=1,s[++m]=n-1;
std::sort(s+1,s+m+1);
for(int i=1;i<m;++i)ch0.push_back(build(s[i],s[i+1]));
p[0]=1;ip[0]=1;
for(int i=1;i<=n;++i)p[i]=1ll*p[i-1]*i%mod;
ip[1]=1;for(int i=2;i<=n;++i)ip[i]=mod-1ll*ip[mod%i]*(mod/i)%mod;
for(int i=1;i<=n;++i)ip[i]=1ll*ip[i-1]*ip[i]%mod;
int ans=1,siz=0;
for(auto i:ch0)if(i)dp(i),ans=1ll*ans*C(siz+R[i]-L[i]-1,siz)%mod*f[i]%mod,siz+=R[i]-L[i]-1;
if(W)printf("%d %d\n",ans1,ans);
else printf("%d\n",ans1);
while(Q--){
int l=gi(),r,k=gi(),t;if(l>k)std::swap(l,k);
r=*std::upper_bound(G[l].begin(),G[l].end(),k);
t=*--std::lower_bound(G[l].begin(),G[l].end(),k);
if(r==n){
if(W)printf("%d %d\n",ans1-1,1ll*ans*invC(ans1,k-l-1)%mod*C(ans1-1,k-l-2)%mod);
else printf("%d\n",ans1-1);
continue;
}
if(W)printf("%d %d\n",ans1-(r==n),1ll*ans*invC(k-l-2,t-l-1)%mod*invC(r-l-2,r-k-1)%mod*C(r-t-2,k-t-1)%mod*C(r-l-2,t-l-1)%mod);
else printf("%d\n",ans1-(r==n));
}
return 0;
}
HNOI2019 多边形 polygon的更多相关文章
- 【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)
[HNOI2019]多边形(模拟,组合计数) 题面 洛谷 题解 突然特别想骂人,本来我考场现切了的,结果WA了几个点,刚刚拿代码一看有个地方忘记取模了. 首先发现终止态一定是所有点都向\(n\)连边( ...
- 结合谷歌地图多边形(polygon)与Sql Server 2008的空间数据类型计算某个点是否在多边形内的注意事项
首先在利用 GEOGRAPHY::STPolyFromText(@GeoStr, 4326) 这样的函数把字符串转换为Geography类型时,字符串里经纬度的顺序是 “经度[空格]纬度”,即“lon ...
- Arcgis api for javascript学习笔记(4.5版本) - 点击多边形(Polygon)并高亮显示
在现在的 arcgis_js_v45_api 版本中并没有直接提供点击Polygon对象高亮显示.需要实现如下几个步骤: 1.点击地图时,获取Polygon的Graphic对象: 2.对获取到的Gra ...
- 百度地图笔记_覆盖物(标注marker,折线polyline,多边形polygon)的点击弹窗和右键菜单
利用绘制工具绘制点线面,并在执行绘制完成回调事件给相应覆盖物添加事件操作,提供标注的点击弹窗和标注.折线.多边形的右键删除 效果图如下: 完整代码如下:html+js <!DOCTYPE htm ...
- [RGEOS]绘制多边形Polygon
绘制OGIS定义的Polygon public void DrawPolygon(Polygon pol, Brush brush, Pen pen, bool clip) { gc = Graphi ...
- luogu P5288 [HNOI2019]多边形
传送门 这是什么神仙操作... 首先要注意一些性质.首先每一个\((x,n)\)的边可以把当前多边形分成两半,这两半的操作是独立的.然后对于某一个没有\((x,n)\)的边的多边形,最优操作是唯一的. ...
- 【洛谷5288】[HNOI2019] 多边形(二叉树模型)
点此看题面 大致题意: 给你一个多边形,用若干不重合.不相交的线段将其划分为若干三角形区域,并定义旋转操作\((a,c)\)为选定\(4\)个点\(a,b,c,d\)满足\(a<b<c&l ...
- css create 多边形 polygon
案例: 代码: element.style { width: 0; height: 0; /* border-left: 50px solid transparent; */ border-rig ...
- [HNOI2019]多边形[二叉树建模、组合计数]
题意 题目链接 分析 不难发现终态一定是 \([2,n-2]\) 中的每个点都与 \(n\) 连边. 关于凸多边形的划分问题,可以将它看作一棵二叉树:每个树点可以看做点可以看做边. 本题中看做点来处理 ...
随机推荐
- wap2app(六)-- wap2app的原生标题头无法隐藏
大概有一个星期没有用HBuilder打包,今天更新了HBuilder版本再继续打包,就出现了一系列问题.其中有一个就是打包之后,除首页外,其他页面多出了原生标题,如下图: 查阅资料,打开 sitema ...
- 【Python】keras使用Lenet5识别mnist
原始论文中的网络结构如下图: keras生成的网络结构如下图: 代码如下: import numpy as np from keras.preprocessing import image from ...
- IDEA插件清单
zookeeper插件,方便查看zk节点信息 Maven Helper,方便解决jar包冲突 Free Mybatis plugin,自动映射mapper接口到对应查询statements gener ...
- Python使用np.c_和np.r_实现数组转换成矩阵
# -*- coding: utf-8 -*-"""Created on Sat Jun 30 14:49:22 2018 @author: zhen"&quo ...
- C#微信公众号开发——获取access_token
access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.正常情况下access_token有效期为7200秒(两个小时),微信获取access_token接 ...
- Unity LayerMask 的位运算
Unity的Layer Unity是用 int32来表示32个Layer层,int32用二进制来表示一共有32位. 0000 0000 0000 0000 0000 0000 0000 0000 31 ...
- java针对不同方法的分页
一.常见的分页实现方式 ①使用List接口中的subList(int startIndex,int endIndex)方法实现分页 ②直接使用数据库SQL语句实现分页 ③使用hibernate等框架实 ...
- 串口-CreateFile的使用
在32位的windows系统中,串口和其他通信设备是作为文件处理的.对串口的操作和对文件的操作是完全一样的.通信以调用CreateFile()开始. 函数原型如下: HANDLE CreateFile ...
- Python3编写网络爬虫11-数据存储方式四-关系型数据库存储
关系型数据库存储 关系型数据库是基于关系模型的数据库,而关系模型是通过二维表保存的,所以它的存储方式就是行列组成的表.每一列是一个字段,每一行是一条记录.表可以看作某个实体的集合,而实体之间存在联系, ...
- jQuery 自定义函数写法分享
时间:02月20日 自定义主要通过两种方式实现$.extend({aa:function(){}});$.fn.extend({aa:function(){}});调用的方法分别是:$.aa(); ...