[HNOI2019]多边形[二叉树建模、组合计数]

题意

题目链接

分析

不难发现终态一定是 \([2,n-2]\) 中的每个点都与 \(n\) 连边。

关于凸多边形的划分问题,可以将它看作一棵二叉树:每个树点可以看做点可以看做边。

本题中看做点来处理,并将与 \(n\) 号点相连的所有节点看作一次分割(这些点之间一定有连边),每个分割出的区间(也是一棵树)里的根连到树的根。

对于第一问,答案为 \(n-3\) 条边中未连接 \(n\) 号点的边数。容易构造一种方案达到下界:

对于树的根,不同的子树每一步有且仅有一个位置满足可以旋转。这个点没有和 \(n\) 相连,且与 \(n\) 的连线 和 1 条线段相交。

所以对每个非根节点有: \(f_u=(s_u-1)!\prod \frac{f_v}{s_v!}\)

对于根节点有:\(f_{rt}=s_{rt}!\prod \frac{f_v}{s_v!}\)

所以对于每个非根节点,在 \(f_{rt}\) 中的贡献都是 \(\frac{(s_u-1)!}{s_u!}=\frac{1}{s_u}\)

所以答案可以写成:\(\frac{ans1!}{\prod\limits_{(l,r)\in E,r \ne n}(r-l-1)}\)

对于 \(m\) 个拓展状态,可以考虑删边和加边,\(a,b,c,d\) 中一定满足 \(b\)\(c\) 所有出边中的次小值, \(d\)\(c\) 所有出边中的最大值,所以每次确定 \(b,d\) 就可以 \(O(1)\) 了。

复杂度 \(O(n+m)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i, a, b) for(int i = a; i <= b; ++i)
inline int gi() {
    int x = 0,f = 1;
    char ch = getchar();
    while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
    return x * f;
}
template <typename T> inline bool Max(T &a, T b){return a < b ? a = b, 1 : 0;}
template <typename T> inline bool Min(T &a, T b){return a > b ? a = b, 1 : 0;}
const int N = 1e5 + 7, mod = 1e9 + 7;
int type, n, ans1, m, ans2 = 1;
int inv[N], L[N], R[N], L2[N];
void upd(int l, int r, int v) {
    if(r == n) return;
    if(v == 1) 
        ans2 = (LL) ans2 * inv[r - l - 1] % mod * (++ans1) % mod;
    else
        ans2 = (LL) ans2 * (r - l - 1) % mod * inv[ans1--] % mod;
}
short num[100];
void print(int x) {
    short len = 0;
    do {
        num[len++] = x % 10;
        x /= 10;
    }while(x);
    for(short i = len - 1; ~i; --i) putchar(num[i] + '0');
}
int main() {
    type = gi(), n = gi();
    inv[1] = 1;
    rep(i, 2, n) inv[i] = (LL) (mod - mod / i) * inv[mod % i] % mod;
    R[1] = n, L[n] = 1;
    rep(i, 2, n - 1) L[i] = i - 1, L2[i] = i, R[i] = i + 1;
    rep(i, 1, n - 3) {
        int x = gi(), y = gi();
        Max(R[x], y);
        if(x < L[y]) {
            L2[y] = L[y], L[y] = x;
        }else Min(L2[y], x);
        upd(x, y, 1);
    }
    m = gi();
    print(ans1); if(type) putchar(' '), print(ans2); puts("");
    while(m--) {
        int a = gi(), c = gi(), b = L2[c], d = R[c];
        upd(a, c, -1);
        upd(b, d, 1);
        print(ans1); if(type) putchar(' '), print(ans2); puts("");
        upd(b, d, -1);
        upd(a, c, 1);
    }
    return 0;
}

转载于:https://www.cnblogs.com/yqgAKIOI/p/10792255.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值