首页 > 其他分享 >「解题报告」AGC023E Inversions

「解题报告」AGC023E Inversions

时间:2023-05-19 20:23:14浏览次数:30  
标签:frac val int mid 解题 MAXN AGC023E Inversions return

好。

首先考虑怎么计算方案数。我们考虑按照 \(a_i\) 从小往大选,设排序后的下标为 \(b_i\),那么容易得出方案数为:

\[s = \prod_{i=1}^n (a_{b_i} - i + 1) \]

我们设 \(c_i = a_{b_i} - i + 1\),这代表着某个数的选择方案数。

然后考虑经典拆贡献,枚举每一对 \((i, j)\),求 \(p_i > p_j\) 的方案数,这样累加起来就是答案。

首先假设 \(a_i < a_j\),我们直接枚举 \(p_i, p_j\) 的选择方案,容易得出为 \(\dbinom{c_i}{2} = \dfrac{c_i(c_i - 1)}{2}\)。然后考虑这会导致 \(a_i\) 与 \(a_j\) 之间的数的选择方案减少 \(1\),其他数的选择方案不变,那么可以写出式子:

\[\begin{aligned} f(i, j) &= \frac{c_i (c_i - 1)}{2} \times \frac{s}{c_i c_j} \times \prod_{a_i < a_k < a_j}\frac{c_k - 1}{c_k}\\ &=\frac{s (c_i - 1)}{2 c_j} \prod_{a_i < a_k < a_j}\frac{c_k - 1}{c_k} \end{aligned} \]

假如说 \(a_i > a_j\),那么我们可以先对称的求出 \(p_i > p_j\) 的方案数,然后总方案数减去它即可,即:

\[f(i, j) =s - \frac{s (c_i - 1)}{2 c_j} \prod_{a_i < a_k < a_j}\frac{c_k - 1}{c_k} \]

我们考虑维护这个东西。我们按照 \(a_i\) 从小往大的顺序依次计算答案,然后以 \(i\) 为下标建一棵线段树,这样我们就可以将 \(i < j\) 与 \(i > j\) 的分开来计算。对于后者,我们还需要统计有多少个数,可以拿树状数组维护。这个式子容易通过区间乘,区间求和计算。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, P = 1000000007;
int qpow(int a, int b) {
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1ll * ans * a % P;
        a = 1ll * a * a % P;
        b >>= 1;
    }
    return ans;
}
int n, a[MAXN], b[MAXN], c[MAXN];
struct SegmentTree {
    struct Node {
        int val, tag;
        Node() : val(0), tag(1) {}
    } t[MAXN << 2];
#define lc (i << 1)
#define rc (i << 1 | 1)
    void tag(int i, int v) {
        t[i].tag = 1ll * t[i].tag * v % P;
        t[i].val = 1ll * t[i].val * v % P;
    }
    void pushDown(int i) { tag(lc, t[i].tag), tag(rc, t[i].tag), t[i].tag = 1; }
    void mul(int a, int b, int v, int i = 1, int l = 1, int r = n) {
        if (a > b) return;
        if (a <= l && r <= b) return tag(i, v);
        int mid = (l + r) >> 1;
        pushDown(i);
        if (a <= mid) mul(a, b, v, lc, l, mid);
        if (b > mid) mul(a, b, v, rc, mid + 1, r);
        t[i].val = (t[lc].val + t[rc].val) % P;
    }
    void set(int d, int v, int i = 1, int l = 1, int r = n) {
        if (l == r) {
            t[i].val = v;
            return;
        }
        int mid = (l + r) >> 1;
        pushDown(i);
        if (d <= mid) set(d, v, lc, l, mid);
        else set(d, v, rc, mid + 1, r);
        t[i].val = (t[lc].val + t[rc].val) % P;
    }
    int query(int a, int b, int i = 1, int l = 1, int r = n) {
        if (a > b) return 0;
        if (a <= l && r <= b) return t[i].val;
        int mid = (l + r) >> 1;
        pushDown(i);
        if (b <= mid) return query(a, b, lc, l, mid);
        if (a > mid) return query(a, b, rc, mid + 1, r);
        return (query(a, b, lc, l, mid) + query(a, b, rc, mid + 1, r)) % P;
    }
} st;
struct BinaryIndexTree {
    int a[MAXN];
#define lowbit(x) (x & (-x))
    void add(int d, int v) {
        while (d <= n) {
            a[d] += v;
            d += lowbit(d);
        }
    }
    int query(int d) {
        if (!d) return 0;
        int ret = 0;
        while (d) {
            ret += a[d];
            d -= lowbit(d);
        }
        return ret;
    }
} bit;
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        b[i] = i;
    }
    sort(b + 1, b + 1 + n, [&](int x, int y) { return a[x] < a[y]; });
    int s = 1;
    for (int i = 1; i <= n; i++) {
        c[i] = a[b[i]] - i + 1;
        if (c[i] <= 0) {
            printf("0\n");
            return 0;
        }
        s = 1ll * s * c[i] % P;
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = (ans + 1ll * st.query(1, b[i] - 1) * qpow(2 * c[i], P - 2)) % P;
        ans = (ans - 1ll * st.query(b[i] + 1, n) * qpow(2 * c[i], P - 2) % P + P) % P;
        ans = (ans + 1ll * (i - bit.query(b[i]) - 1) * s) % P;
        st.mul(1, n, 1ll * (c[i] - 1) * qpow(c[i], P - 2) % P);
        st.set(b[i], 1ll * s * (c[i] - 1) % P);
        bit.add(b[i], 1);
    }
    printf("%d\n", ans);
    return 0;
}

标签:frac,val,int,mid,解题,MAXN,AGC023E,Inversions,return
From: https://www.cnblogs.com/apjifengc/p/17416195.html

相关文章

  • 「解题报告」UOJ671 [UNR #5] 诡异操作
    这题怎么这么多差评啊?哦卡常啊,没事了。发现两个操作都是只增不减,显然势能线段树。考虑维护区间按位与,线段树上维护每一位上有多少个\(1\),按位与就是区间赋\(0\),对于区间除法暴力重构。直接暴力维护即可做到\(O((n+q)\logn\logV)\)的复杂度。但是\(\logV=128\),顶两......
  • 一篇看懂递归的套路解题法
    递归所谓递归,不过是将一个复杂问题分解为一个更小的问题进行求解,在这里我们不再扯太多犊子了,网上有太多递归的介绍让人眼花缭乱摸不着头脑,我们直接开始讲解递归的解体思路。第一步:求解最基本问题并将其返回这一步也就是网上所谓的递归出口,但是个人认为递归出口不太能很好的描述......
  • 「解题报告」P3703 [SDOI2017]树点涂色
    有趣题,代码超好写,而且思路超有趣!!!首先发现操作1的操作都是某个点到根,不难发现这样每种颜色一定对应着树上的一条链。那么操作2可以直接树上查分求答案,这样我们只需要考虑维护每个点到根的链的数量了。怎么维护链的数量?发现这个操作1长得和LCT的Access操作一模一样啊,所......
  • [AGC013C] Ants on a Circle 解题报告
    洛谷题面AT题面CF625F先考虑弱化版,若是不考虑编号怎么办。这个问题有一个很经典的结论,碰撞等同穿过,所以直接算出每个点按照指定方向走,在\(t\)秒后的位置即可。现在多了一个编号,因为是碰撞,所以两个点的相对位置是相同的,即\(x\)号点原来是\(y\)号点顺时针方向的第几个点,......
  • [NOI2018] 归程 解题报告
    题面步行的最小距离很容易求,dij随便求一下每个点的最短路,然后找到与\(v\)能相互坐车到达的点,对这些点的最短路都有可能是答案,取个\(\min\)即可。所以本题最大的问题是怎么找到在水位线为\(p\)时,与\(v\)能相互坐车到达的点。可以想到只保留海拔大于\(p\)的边,因为只要考......
  • 解题记录1~4月份
    #1月开始打```atcoder```##abc132###$a,b,c$题:由于过于简单,直接跳过。###$d$题:插板法,把篮球分为$i$块,也就是插$i-1$块板子,所以可能性就有$\dbinom{k-1}{i-1}$种,红球同理可得$\dbinom{n-k-1}{i-1}$。###$e$题:把一条边拆成三条边,然后再跑一遍```dijkstra```即可......
  • 「解题报告」ARC103D Distance Sums
    给Kaguya看了一眼,Kaguya用了一分钟切了。我看了一个小时。这就是神吗。考虑一个点往叶子走答案的贡献,显然距离和会变化\(-siz_u+(n-siz_u)=n-2siz_u\)。如果我们以重心为根,那么所有的\(n-2siz_u>0\),那么这实际上是一个小根堆。那么我们考虑从大往小枚举叶子,然......
  • [网络安全]AntSword蚁剑实战解题详析
    免责声明:本文仅分享AntSword渗透相关知识,不承担任何法律责任。请读者自行安装蚁剑,本文不再赘述。蚁剑介绍蚁剑(AntSword)是一款开源的跨平台WebShell管理工具,它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员。中国蚁剑的特点主要有如下几点:1.支持多平台......
  • [网络安全]BurpSuite爆破实战解题详析之BUUCTF Brute 1
    免责声明:本文仅分享AntSword渗透相关知识,不承担任何法律责任。请读者自行安装BurpSuite,本文不再赘述。在用户名和密码都未知的情况下,进行用户名、密码的组合爆破,效率极低。先爆破用户名,再利用得到的用户名爆破密码,将提高爆破速度。BUUCTFBrute1题目操作Burp抓包单独......
  • [网络安全]DVWA之File Upload—AntSword(蚁剑)攻击姿势及解题详析合集
    免责声明:本文仅分享SQL攻击相关知识,不承担任何法律责任。DVWA、BurpSuite请读者自行安装,本文不再赘述。同类文章参考:[网络安全]AntSword(蚁剑)实战解题详析(入门)FileUpload—lowlevel源码中无过滤:上传包含一句话木马<?php@eval($_POST[qiushuo]);?>的文件qiu.php回显......