【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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM