M_sea

洛谷5288 [HNOI2019]多边形
LuoguBZOJLOJ分析神仙题。orz 考场(几乎)切掉此题的 yyb首先有一个结论:最终状态一定满足所有边都...
扫描右侧二维码阅读全文
25
2019/05

洛谷5288 [HNOI2019]多边形

Luogu

BZOJ

LOJ

分析

神仙题。orz 考场(几乎)切掉此题的 yyb

首先有一个结论:最终状态一定满足所有边都连向 $n$ 。那么步数就等于初始状态下不与 $n$ 相连的边数。

把所有初始与 $n$ 相连的边按另一个端点排序。那么对于排序后相邻的两个点 $l,r(l+1\neq r)$ ,一定会存在一条边 $(l,r)$ 。那么图中一定会存在一个点 $mid$ ,使得初始状态下存在两条边 $(l,mid)$ 和 $(mid,r)$ 。那么我们旋转一次后就可以得到 $(mid,n)$ 。然后整个区间 $[l,r]$ 会变成两个区间 $[l,mid]$ 和 $[mid,r]$ 。

可以发现这构成了一棵二叉树。那么把所有 $l,r$ 拿出来就构成了一片二叉树森林。

考虑方案数如何计算。显然每个节点只有在其父亲被操作后才能操作,那么每个节点的贡献为 $\large\frac{(sz[ls]+sz[rs])!}{sz[ls]!sz[rs]!}$ ,直接乘到一起就是方案数了。

考虑修改怎么做。第一种情况是旋转了一条边 $(i,n)$ ,此时相当于删去了一棵二叉树的根节点,那么最优步数会减 $1$ ;第二种情况是旋转了一条端点不为 $n$ 的边,此时相当于 $\mathrm{rotate}$ 了一棵子树(类似 $\mathrm{splay}$ 里那个 $\mathrm{rotate}$ ),显然最优步数是不变的。

修改后的方案数相当于除去以前的贡献再乘上现在的贡献,画个图就可以推出来了。

具体实现和细节见代码。

代码

// =================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// =================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <map>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=100000+10;
const int mod=1e9+7;

inline int qpow(int a,int b) {
    int c=1;
    for (;b;b>>=1,a=1ll*a*a%mod)
        if (b&1) c=1ll*c*a%mod;
    return c;
}

int W,n,m;
int fac[N],ifac[N];
vector<int> E[N];
int fa[N],ls[N],rs[N],sz[N];
int tot=0,sum=0,ans=1;
map<pair<int,int>,int> M;

inline int calc(int n,int m) {
    return 1ll*fac[n+m]*ifac[n]%mod*ifac[m]%mod;
}
inline int calcinv(int n,int m) {
    return 1ll*ifac[n+m]*fac[n]%mod*fac[m]%mod;
}

inline void addEdge(int u,int v) {
    E[u].push_back(v),E[v].push_back(u);
}

inline void dfs(int& x,int l,int r,int f) {
    if (l+1==r) return;
    x=++tot,M[make_pair(l,r)]=x,fa[x]=f;
    int mid=*(--lower_bound(E[l].begin(),E[l].end(),r));
    dfs(ls[x],l,mid,x),dfs(rs[x],mid,r,x);
    sz[x]=sz[ls[x]]+sz[rs[x]]+1;
    ans=1ll*ans*calc(sz[ls[x]],sz[rs[x]])%mod;
}

int main() {
    fac[0]=1;
    for (re int i=1;i<N;++i) fac[i]=1ll*fac[i-1]*i%mod;
    ifac[N-1]=qpow(fac[N-1],mod-2);
    for (re int i=N-1;i;--i) ifac[i-1]=1ll*ifac[i]*i%mod;
    
    W=read(),n=read();
    for (re int i=1;i<n;++i) addEdge(i,i+1);
    addEdge(n,1);
    for (re int i=1;i<=n-3;++i) {
        int u=read(),v=read();
        addEdge(u,v);
    }
    for (re int i=1;i<=n;++i) sort(E[i].begin(),E[i].end());

    for (re int i=0;i<E[n].size()-1;++i) {
        int x=0; dfs(x,E[n][i],E[n][i+1],0);
        ans=1ll*ans*calc(sum,sz[x])%mod,sum+=sz[x];
    }

    if (W) printf("%d %d\n",sum,ans);
    else printf("%d\n",sum);
    
    m=read();
    while (m--) {
        int a=read(),b=read(),x=M[make_pair(a,b)];
        if (fa[x]) {
            int f=fa[x],now=ans;
            now=1ll*now*calcinv(sz[ls[x]],sz[rs[x]])%mod;
            now=1ll*now*calcinv(sz[ls[f]],sz[rs[f]])%mod;
            now=1ll*now*calc(sz[rs[x]],sz[rs[f]])%mod;
            now=1ll*now*calc(sz[ls[x]],sz[rs[x]]+sz[rs[f]]+1)%mod;
            if (W) printf("%d %d\n",sum,now);
            else printf("%d\n",sum);
        } else {
            int now=ans;
            now=1ll*now*calcinv(sz[ls[x]],sz[rs[x]])%mod;
            now=1ll*now*calcinv(sum-sz[x],sz[x])%mod;
            now=1ll*now*calc(sum-sz[x],sz[ls[x]])%mod;
            now=1ll*now*calc(sum-sz[x]+sz[ls[x]],sz[rs[x]])%mod;
            if (W) printf("%d %d\n",sum-1,now);
            else printf("%d\n",sum-1);
        }
    }
    
    return 0;
}
最后修改:2019 年 05 月 26 日 10 : 14 PM

发表评论