zoukankan      html  css  js  c++  java
  • 【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
    ",cnt);
    	else printf("%d %d
    ",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
    ",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
    ",pans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    WinCE NAND flash
    正确选择报表工具的十大标准
    从技术岗位走向管理岗位:机会是留给有准备的人
    创业失败的七个原因及解决之道
    技术人员如何参与产品设计讨论:激活那一潭死水
    基于Android Studio搭建hello world工程
    基于Android Studio搭建Android应用开发环境
    JS数组去重的6种算法实现
    八款前端开发人员更轻松的实用在线工具
    海市蜃楼-嘉兴外蒲岛奇遇
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10668978.html
Copyright © 2011-2022 走看看