「HNOI2019」多边形
该文被密码保护。
题目描述
题解
首先我们需要解决的问题是找到旋转\((a,c)\)对应的\(b,d\)是多少。
发现\(b\)在区间\([a,c]\)中,可以模拟,依次削掉多边形的最外层的角。
求\(d\)的话观察到每一对之前求得的\((a,b,c)\),都有\((a,b)\)旋转的\(“d”\)点是\(c\)。除此之外就只有可能漏算和\(1,n\)相关的一些神奇情况,特判好啦。具体见代码。
然后观察样例图,感觉到最后应该把每条边的一个端点旋到\(n\)。于是可以把多边形按已经连向\(n\)的边切成若干块,每一块可以单独讨论。
因为每次旋转至多改变一条边的位置,因此希望每旋一下都能多一条连向\(n\)的边。假设当前\(l,r\)两点已经连向\(n\),并且\([l,r]\)之间没有其他连向\(n\)的边。那么此时一定有一条边\((l,r)\)(否则不满足没有其他连向\(n\)的边)。旋转\((l,r)\)恰好多一条边,并且除此之外区间内没有其他边可以。设之前求出旋转\((l,r)\)的\(“b”\)点为\(p\),在操作次数最少的前提下完成区间\([l,r]\)的方案数为\(F(l,r)\),区间\([l,r]\)的最少次数为\(G(l,r)\),则\(F(l,r)=F(l,p)*F(p,r)*C_{G(l,p)+G(p,r)}^{G(l,p)},G(l,r)=G(l,p)+G(p,r)+1\)。
机智的Newuser发现\(G(l,r)=r-l-1\),最少操作次数就是总边数减去已经连好的边数。
假设之前多边形被拆成了k部分,分界点依次为\(p_1,p_2,p_3,…,p_{k+1}\),最少Ans次操作完,则方案数为\([\Pi_{i=1}^{k}F(p_{i},p_{i+1})]*\frac{Ans! }{\Pi_{i=1}^{k} G( p_{i} , p_{i+1} )! }\)(后面那东西可以用组合数展开得到)。
仔细观察,发现每条边\((a,b)\)只要和n不接触,他们的\(G\)值的贡献都是一样的,都为\(\frac{(G(a,b)-1)!}{G(a,b)!}\)。
upd:为什么我这么zz啊,那东西不就是\(\frac{1}{G(a,b)}\)吗。。。
然后我们就可以处理修改了。
对于旋转以后对Ans有影响(其实就是减一)的操作,我们发现这条边旋转以后没有贡献了,减去即可。
否则,因为F区间长得像二叉树,故对\(F(a,b)\)画图,我们发现只有边\((a,c)\)变成了\((b,d)\),其他边在树上的位置变了,但是我们知道树上位置对贡献没有影响。改一改就好啦。。似乎有点虎头蛇尾呢,因为我急着睡觉啊QaQ
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int Q=1<<17,MOD=1e9+7;
int fac[Q],ifac[Q];
inline int mul(int a,int b)
{return 1LL*a*b%MOD;}
int ksm(int a,int b)
{
int ans=1;
for(;b;b>>=1,a=mul(a,a))
if(b&1)ans=mul(ans,a);
return ans;
}
int L[Q],R[Q];
int pl[Q],pr[Q];
struct dt{
int id,len;
}cmp[Q];
bool cmp1(dt a,dt b)
{return a.len<b.len;}
int in[Q],out[Q];
map<int,int >cov[Q];
int ty,n,ans,pi;
void Print()
{
int val=mul(pi,fac[ans]);
if(ty==1)printf("%d %d\n",ans,val);
else printf("%d\n",ans);
}
void Calc(int x)
{pi=mul(pi,mul(fac[x-1],ifac[x]));}
void ICalc(int x)
{pi=mul(pi,mul(ifac[x-1],fac[x]));}
int main()
{
scanf("%d%d",&ty,&n);
ans=n-3;
//
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=mul(i,fac[i-1]);
ifac[n]=ksm(fac[n],MOD-2);
for(int i=n;i;--i)
ifac[i-1]=mul(ifac[i],i);
//
for(int i=1;i<=n-3;i++){
scanf("%d%d",&L[i],&R[i]);
if(L[i]>R[i])swap(L[i],R[i]);
cov[L[i]][R[i]]=i;
ans-=(R[i]==n);
}
//
for(int i=1;i<=n-3;i++)
cmp[i]=(dt){i,R[i]-L[i]};
sort(cmp+1,cmp+n-2,cmp1);
for(int i=1;i<=n;i++)
pl[i]=i-1,pr[i]=i+1;
pl[1]=n,pr[n]=1;
for(int i=1;i<=n-3;i++){
int id=cmp[i].id,l=L[id],r=R[id];
in[id]=pr[l];
pr[l]=r,pl[r]=l;
}
//
for(int i=1;i<=n-3;i++){
out[cov[L[i]][in[i]]]=R[i];
out[cov[in[i]][R[i]]]=L[i];
}
for(int i=1;i<=n-3;i++)
if(!out[i]){
if(L[i]==1)out[i]=n;
else out[i]=1;
}
//
pi=1;
for(int i=1;i<=n-3;i++)
if(R[i]!=n)Calc(R[i]-L[i]-1);
Print();
int m;
for(scanf("%d",&m);m;--m){
int a,b;
scanf("%d%d",&a,&b);
if(a>b)swap(a,b);
int id=cov[a][b],len=b-a-1;
if(out[id]==n){
--ans;
ICalc(len);
Print();
Calc(len);
++ans;
}
else{
int ano=out[id]-in[id]-1;
ICalc(len),Calc(ano);
Print();
Calc(len),ICalc(ano);
}
}
return 0;
}