[loj3056][hnoi2019]多边形

11 篇文章 0 订阅

前言

模数打错导致在模拟赛时变成40分

题目相关

链接

题目大意

给定一个多边形的三角划分
定义一个旋转操作为对于四个有边相连的点 a &lt; b &lt; c &lt; d a&lt;b&lt;c&lt;d a<b<c<d,原本 a ↔ c a\leftrightarrow c ac连边,旋转后 b ↔ d b\leftrightarrow d bd连边
给定一幅 n n n个点的多边形三角划分图,求至少旋转多少次才能使这个图内不能再旋转,并输出这样旋转的方案数
给出的图分为两种,一种是原图,还有一种是在原图中给出一条边 a ↔ c a\leftrightarrow c ac,将其旋转得到的图(显然一条边的旋转唯一),询问图的数量为 m m m

数据范围

n , m ≤ 100000 n,m\le100000 n,m100000

题解

首先定义 d i d_i di i i i号点的度数(不包含多边形上的边)

首先考虑终止状态,观察样例,我们发现所有边都连向 n n n号点时就不能再转了
我们发现每次肯定能将一条端点中不含 n n n的边转到含 n n n
具体操作是选定一条边 a ↔ b a\leftrightarrow b ab,其两侧的三角形分别为 Δ a b c \Delta abc Δabc Δ a b n \Delta abn Δabn这样就可以把边转为 c ↔ n c\leftrightarrow n cn
我们发现这样一来,第一问的答案就是 n − 3 − d n n-3-d_n n3dn

我们考虑第二问,首先,我们发现边的可旋转顺序构成的拓扑图是一棵树(这一点画画图就知道了)
然后我们考虑方案数如何计算
我们仔细想发现这是经典套路:[loj6391][THUPC2018]淘米神的树(Tommy)
我们考虑一棵树的子树方案数方案数(这棵子树的根一定要第一个做,剩下的按子树内部考虑即可)
a n s u = ( s i z e u − 1 ) ! ∏ f a v = u a n s v s i z e v ! ans_u=(size_u-1)!\prod_{fa_v=u}\frac{ans_v}{size_v!} ansu=(sizeu1)!fav=usizev!ansv
进行整理
a n s u = ( s i z e u − 1 ) ! ∏ f a v = u s i z e v ! ∏ f a v = u a n s v ans_u=\frac{(size_u-1)!}{\prod_{fa_v=u}size_v!}\prod_{fa_v=u}{ans_v} ansu=fav=usizev!(sizeu1)!fav=uansv
根节点额外考虑,根节点是
a n s r o o t = s i z e r o o t ! ∏ f a v = r o o t a n s v s i z e v ! ans_{root}=size_{root}!\prod_{fa_v=root}\frac{ans_v}{size_v!} ansroot=sizeroot!fav=rootsizev!ansv
代入,我们发现每个节点都是一个节点的儿子,也是一个自己,这样的话每个节点的贡献就是 ( s i z e u − 1 ) ! s i z e u = 1 s i z e u \frac{(size_u-1)!}{size_u}=\frac1{size_u} sizeu(sizeu1)!=sizeu1
a n s r o o t = s i z e r o o t ! ∏ u ≠ r o o t s i z e u ans_{root}=\frac{size_{root}!}{\prod_{u\neq root} size_u} ansroot=u̸=rootsizeusizeroot!
我们发现一条边 u ↔ v u\leftrightarrow v uv代表节点的size就是 ∣ u − v ∣ − 1 |u-v|-1 uv1
a n s 1 ans_1 ans1为第一问的答案, a n s 2 ans_2 ans2为第二问的答案
a n s 2 = a n s 1 ! ∏ u ↔ v , u ≠ n , v ≠ n ∣ u − v ∣ − 1 ans_2=\frac{ans_1!}{\prod_{u\leftrightarrow v,u\neq n,v\neq n}|u-v|-1} ans2=uv,u̸=n,v̸=nuv1ans1!
我们发现这个很好维护,只要找到一次旋转从哪条边转到了哪条边即可,用个set维护一下每个点的出边,然后在set中找连边目标点的编号相邻元素就是了

代码

代码很清真

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<set>
#include<vector>
typedef long long ll;
#define rg register
template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
const int maxn=100001,mod=1000000007;
int W,n,m,d[maxn];
std::set<int>e[maxn];
ll pow(ll x,ll y)
{
	ll res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
ll ans,fac[maxn],inv[maxn];
ll C(const int n,const int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
void calc()
{
	print(n-3-d[n]);
	if(W)putchar(' '),print(fac[n-3-d[n]]*pow(ans,mod-2)%mod);
	putchar('\n');
}
inline void del(const int u,const int v)
{
	d[u]--,d[v]--;
	if(u!=n&&v!=n)ans=ans*pow(abs(u-v)-1ll,mod-2)%mod;
}
inline void add(const int u,const int v)
{
	d[u]++,d[v]++;
	if(u!=n&&v!=n)ans=ans*(abs(u-v)-1)%mod;
}
int main()
{
//	freopen("polygon.in","r",stdin);freopen("polygon.out","w",stdout);
	fac[0]=1;
	for(rg int i=1;i<=100000;i++)fac[i]=fac[i-1]*i%mod;
	inv[100000]=pow(fac[100000],mod-2);
	for(rg int i=100000;i>=1;i--)inv[i-1]=inv[i]*i%mod;
	ans=1;
	read(W),read(n);
	for(rg int i=1;i<=n-3;i++)
	{
		int u,v;read(u),read(v);
		e[u].insert(v),e[v].insert(u);
		add(u,v);
	}
	e[n].insert(1),e[1].insert(n);
	for(rg int i=1;i<n;i++)e[i].insert(i+1),e[i+1].insert(i);
	calc();
	read(m);
	for(rg int i=1;i<=m;i++)
	{
		int a,b,na,nb;read(a),read(b);
		del(a,b);
		std::set<int>::iterator Pos=e[a].find(b),Posl,Posr;
		Posl=Pos,Posl--;
		Posr=Pos,Posr++;
		na=*Posl,nb=*Posr;
		add(na,nb);
		calc();
		del(na,nb);
		add(a,b);
	}
	return 0;
}

总结

要想到拓扑的结果是一棵树,然后推一波式子就好了
好像还可以用splay搞,听说本质相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值