「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;
}
posted @ 2019-04-25 00:07 蒟蒻小果冻 阅读(...) 评论(...) 编辑 收藏