【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)
[HNOI2019]多边形(模拟,组合计数)
题面
题解
突然特别想骂人,本来我考场现切了的,结果WA了几个点,刚刚拿代码一看有个地方忘记取模了。
首先发现终止态一定是所有点都向\(n\)连边(看样例图解就知道了)
那么大力猜想一下第一问的答案一定是\(n-3-\)和\(n\)号点直接相连的边数。
手玩一下,发现这样一件事情:和\(n\)直接相连的所有边把多边形分割成了若干个区间,每个区间都用\([l,r]\)表示。
对于\([l,r]\)这个区间,因为已经分割出来了,也就是除了\(l-n,r-n\)之外,没有直接和\(n\)相连的边,那么发现这里执行一次旋转操作必定会选择到\((l,r)\),那么只需要找到\(b\)点,显然\(b\)也是唯一确定的,那么直接在\(l\)的出边中找到小于\(r\)的最大值就行了,这个点就是\(b\)。
发现这次操作执行完之后,这个区间被划分成了两个部分,只需要递归处理就行了。
于是,除了一开始就和\(n\)号点直接相连的边之外,每次划分一定把区间分割成两个部分,并且分割操作唯一,因此我们可以把这个过程用一个二叉树来表示。
考虑计算方案数,一个节点表示这个点所代表的操作必须在左右两个儿子之前进行,而分割完这次之后,左右两个儿子之间就独立了,因此等价于左侧有一个操作序列,右侧有一个操作序列,需要把他们两合并,这里贡献的方案数就是一个组合数。所以方案数就是每个节点合并两个儿子的方案数的乘积。最后再把所有被和\(n\)相连的边划分出来的区间再乘一下拼接的组合数就是答案。
那么我们可以处理单次询问了。
继续考虑提前执行一次旋转操作对于答案的影响,类似\(Splay\),发现这次操作就是把一个点\(rotate\)一下(感性理解或者手玩一下就知道为什么了)。
那么这里需要分类讨论,如果这个点存在父亲,直接\(rotate\)就行了,除掉原本的贡献再乘上新的贡献就可以了。
否则这个点不存在父亲,即这个点是划分的第一次操作,提前旋转之后就变成了和\(n\)相连的边,这里会把第一问的答案减一,然后把原本的方案数除掉,再乘上直接把左右两个儿子当成被和\(n\)相连的边分割的方案数就行了。
时间复杂度一个\(log\)。(因为我要用\(map\)储存每个点对应的是哪条边,以及在建树的时候需要\(lower\_bound\))
下面是考场代码(把那个鬼取模给补上了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
#define ll long long
#define MAX 200200
#define MOD 1000000007
inline int read()
{
	int x=0;char ch=getchar();bool fl=false;
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')fl=true,ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
	return fl?-x:x;
}
int W,n,ans,Ans=1;
int jc[MAX],inv[MAX],jv[MAX];
int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int InvC(int n,int m){return 1ll*jv[n]*jc[m]%MOD*jc[n-m]%MOD;}
int Merge(int n,int m){return C(n+m,n);}
int InvMerge(int n,int m){return InvC(n+m,n);}
int ch[MAX][2],tot,sz[MAX],fa[MAX];
int rt[MAX];
vector<int> E[MAX];
map<pair<int,int>,int> M;
void Divide(int &x,int ff,int l,int r)
{
	if(r-l<=1)return;x=++tot;sz[x]=1;fa[x]=ff;
	int p=lower_bound(E[r].begin(),E[r].end(),l+1)-E[r].begin();
	p=E[r][p];M[make_pair(l,r)]=x;
	Divide(ch[x][0],x,l,p);Divide(ch[x][1],x,p,r);
	sz[x]+=sz[ch[x][0]]+sz[ch[x][1]];
	Ans=1ll*Ans*Merge(sz[ch[x][0]],sz[ch[x][1]])%MOD;
}
int main()
{
	freopen("polygon.in","r",stdin);
	freopen("polygon.out","w",stdout);
	W=read();n=read();
	jc[0]=jv[0]=inv[0]=inv[1]=1;
	for(int i=2;i<=n+n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n+n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=1;i<=n+n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	for(int i=1;i<=n-3;++i)
	{
		int u=read(),v=read();
		E[u].push_back(v);
		E[v].push_back(u);
	}
	for(int i=2;i<n;++i)E[i].push_back(i-1),E[i].push_back(i+1);
	E[1].push_back(2);E[1].push_back(n);
	E[n].push_back(1);E[n].push_back(n-1);
	for(int i=1;i<=n;++i)sort(E[i].begin(),E[i].end());
	for(int i=0,l=E[n].size();i<l-1;++i)Divide(rt[i],0,E[n][i],E[n][i+1]);
	int SS=0;
	for(int i=0,l=E[n].size();i<l-1;++i)Ans=1ll*Ans*Merge(SS,sz[rt[i]])%MOD,SS+=sz[rt[i]];
	int cnt=n-1-E[n].size();
	if(!W)printf("%d\n",cnt);
	else printf("%d %d\n",cnt,Ans);
	int Q=read();
	while(Q--)
	{
		int a=read(),b=read();if(b<a)swap(a,b);
		int p=M[make_pair(a,b)];
		int pcnt=cnt-(fa[p]?0:1);
		if(!W){printf("%d\n",pcnt);continue;}
		else printf("%d ",pcnt);
		int pans=Ans;
		if(fa[p])
		{
			int f=fa[p],k=ch[f][1]==p;
			pans=1ll*pans*InvMerge(sz[ch[p][0]],sz[ch[p][1]])%MOD;
			pans=1ll*pans*InvMerge(sz[ch[f][0]],sz[ch[f][1]])%MOD;
			pans=1ll*pans*Merge(sz[ch[f][k^1]],sz[ch[p][k^1]])%MOD;
			pans=1ll*pans*Merge(sz[f]-sz[p]+sz[ch[p][k^1]],sz[ch[p][k]])%MOD;
		}
		else
		{
			pans=1ll*pans*InvMerge(sz[ch[p][0]],sz[ch[p][1]])%MOD;
			pans=1ll*pans*InvMerge(SS-sz[p],sz[p])%MOD;
			pans=1ll*pans*Merge(SS-sz[p],sz[ch[p][0]])%MOD;
			pans=1ll*pans*Merge(SS-sz[p]+sz[ch[p][0]],sz[ch[p][1]])%MOD;
		}
		printf("%d\n",pans);
	}
	return 0;
}
												
											【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)的更多相关文章
- 3.29省选模拟赛 除法与取模 dp+组合计数
		
LINK:除法与取模 鬼题.不过50分很好写.考虑不带除法的时候 其实是一个dp的组合计数. 考虑带除法的时候需要状压一下除法操作. 因为除法操作是不受x的大小影响的 所以要状压这个除法操作. 直接采 ...
 - HNOI2019 多边形 polygon
		
HNOI2019 多边形 polygon https://www.luogu.org/problemnew/show/P5288 这题镪啊... 首先堆结论: 显然终止状态一定是所有边都连向n了 根据 ...
 - [ZJOI2010]排列计数 (组合计数/dp)
		
[ZJOI2010]排列计数 题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有 ...
 - 一道组合数问题--出自 曹钦翔_wc2012组合计数与动态规划
		
一道组合数问题--出自 曹钦翔_wc2012组合计数与动态规划 [问题描述] 众所周知,xyc 是一个宇宙大犇,他最近在给他的学弟学妹们出模拟赛. 由于 xyc 实在是太巨了,他出了一套自认为很水的毒 ...
 - FJOI2020 的两道组合计数题
		
最近细品了 FJOI2020 的两道计数题,感觉抛开数据范围不清还卡常不谈里面的组合计数技巧还是挺不错的.由于这两道题都基于卡特兰数的拓展,所以我们把它们一并研究掉. 首先是 D1T3 ,先给出简要题 ...
 - bzoj 2281 [Sdoi2011]黑白棋(博弈+组合计数)
		
黑白棋(game) [问题描述] 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色 ...
 - BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]
		
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
 - BZOJ 4555: [Tjoi2016&Heoi2016]求和 [FFT 组合计数 容斥原理]
		
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
 - [总结]数论和组合计数类数学相关(定理&证明&板子)
		
0 写在前面 0.0 前言 由于我太菜了,导致一些东西一学就忘,特开此文来记录下最让我头痛的数学相关问题. 一些引用的文字都注释了原文链接,若侵犯了您的权益,敬请告知:若文章中出现错误,也烦请告知. ...
 
随机推荐
- 从.Net到Java学习第六篇——SpringBoot+mongodb&Thymeleaf&模型验证
			
SpringBoot系列目录 SpringBoot整合mongodb MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.如果你没用过Mong ...
 - 通过ADB查看当前Activity
			
cmd命令中输入:adb shell dumpsys activity activities 在一连串的输出中找到Runing activities com.android.settings是包名. ...
 - 使用GRPC远程服务调用
			
远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程.如 ...
 - centos服务器如何监控访问ip,并将非法ip通过防火墙禁用
			
centos服务器如何监控访问ip,并将非法ip通过防火墙禁用 上周给朋友帮忙,上架了一款小游戏(年年有鱼),项目刚一上线,就遇到了ddos攻击,阿里云连续给出了6次ddos预警提示,服务器一度处于黑 ...
 - MongoDB副本集及C#程序的连接配置
			
1.副本集 高可用是绝大多数数据库管理系统的核心目标之一.如果要想生产数据在发生故障后依然可用,就需要确保为生产数据库多部署一台服务器.MongoDB副本集提供了数据的保护.高可用和灾难恢复的机制. ...
 - 消息 4900,级别 16,状态 2,第 1 行 对表 'XX.XXX' 执行 ALTER TABLE SWITCH 语句失败。对于已启用更改跟踪的表,不可能切换其分区。请先禁用更改跟踪,再使用 ALTER TABLE SWITCH。
			
问题描述: 今天处理切换分区数据的时候出现了这个错误: 消息 4900,级别 16,状态 2,第 1 行 对表 'XX.XXX' 执行 ALTER TABLE SWITCH 语句失败.对于已启用更改跟 ...
 - 让 Windows7 - 64bit 支持 VC++ 6.0 的解决方法(无法启动此程序,因为计算机中丢失 MSVCRTD.dll。尝试重新安装该程序以解决此问题)
			
源地址:https://www.cnblogs.com/poissonnotes/p/4372136.html 无法启动此程序,因为计算机中丢失 MSVCRTD.dll.尝试重新安装该程序以解决此问题 ...
 - Bootstrap -- 导航栏样式、分页样式、标签样式、徽章样式
			
Bootstrap -- 导航栏样式.分页样式.标签样式.徽章样式 1. 使用图标的导航栏 使用导航栏样式: <!DOCTYPE html> <html> <head&g ...
 - swoole多端口监听
			
今天测试swoole写webserver实现多端口监听.记录下爬过的坑:关于tcp协议监听触发不到receive!!!!! 首先上服务端代码 class Http { /** * 服务实例 * @va ...
 - web框架开发-快速认识Django中间件
			
中间件 中间件的概念 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出. 因为改变的是全局,所以需要谨慎实用,用不 ...