首页 > 其他分享 >AT_agc030_d [AGC030D] Inversion Sum 题解

AT_agc030_d [AGC030D] Inversion Sum 题解

时间:2023-08-27 14:34:09浏览次数:40  
标签:Inversion ch int 题解 位置 base agc030 dp

AT_agc030_d [AGC030D] Inversion Sum 题解

题目大意

给你一个长度为 \(n\) 的数列,然后给你 \(q\) 次交换操作,你每次可以选择操作或者不操作,问所有情况下逆序对的总和。(\(n, q \le 3000\))

分析

很容易想到 \(dp\),但是发现不好直接算方案。所以我们用一个小技巧,将求方案数转化为求期望,然后求出最终答案。那么就来考虑怎么设计期望 \(dp\)。

题解

我们先来想怎么求某一情况下期望的逆序对数。

第一反应想到计数,用 \(dp_{i, j}\) 来代表数对 \((i, j)\) 对最终答案作出的贡献。但是在写转移时发现有一点麻烦,于是浅改一下思路,用 \(dp_{i, j}\) 来代表 位置 \(i\) 上的数比位置 \(j\) 上的数大的概率。

我们很容易得到,某一情况下期望的逆序对数就是 \(\displaystyle \sum_{i = 1}^{n} \sum_{j = i + 1}^{n} dp_{i, j}\)。

\(dp\) 的初始值比较好想,就是 \(O(n^2)\) 跑一遍,对于每一个 \(i\) 位置上的数大于 \(j\) 位置上的数的情况,把 \(dp_{i, j}\) 赋成 \(1\) 即可。接下来我们考虑操作一次会使 \(dp\) 怎样变化。假设我们这一次操作所交换的两个位置是 \(x\) 和 \(y\),\(x\) 位置上的数为 \(a_x\),\(y\) 位置上的数为 \(a_y\)。

首先列一个非常显然的式子:

\[\begin{aligned} dp_{x, t} &= 1 - dp_{t, x}\\ dp_{x, t} &= 1 - dp_{t, x} \end{aligned} \]

有了这个式子,我们就能容易地想到 \(dp_{x, y}\) 和 \(dp_{y, x}\) 的转化。首先我们想到,\(a_x\) 和 \(a_y\) 的关系有两种可能,相等或不相等。如果不相等,交换 \(x\) 和 \(y\) 以后,\(dp_{x, y}\) 和 \(dp_{y, x}\) 都会被直接赋值成 \(0.5\),这很好理解。如果相等,那么两者都是 \(0\)。又因为上面的那两个式子,所以我们可以直接简写成 \(\displaystyle dp_{x, y} = dp_{y, x} = \frac{dp_{x, y} + dp_{y, x}}{2}\)。

而对于其他的位置上的数,我们再设一个位置 \(t(1 \le t \le n \land t \neq x \land t \neq y)\)(\(\land\) 是“且”的意思,\(\lor\) 是“或”的意思),其位置上的数为 \(a_t\),那么一定有:

\[\begin{aligned} dp_{x, t} &= 1 - dp_{t, x}\\ dp_{x, t} &= 1 - dp_{t, x}\\ dp_{t, x} = dp_{t, y} &= \frac{dp_{t, x} + dp_{t, y}}{2}\\ dp_{x, t} = dp_{y, t} &= \frac{dp_{x, t} + dp_{y, t}}{2} \end{aligned} \]

前两个式子显然成立(但是好像这个题没用上),我们考虑怎么去证明后两个式子。其实也很好证,因为如果两者换位成功,则 \(dp_{t, x} = dp_{t, y}\),否则不变。而我们又知道,成功的几率为 \(0.5\),那么 \(\displaystyle dp_{t, x} = \frac{dp_{t, x} + dp_{t, y}}{2}\) 显然成立。其他的同理。

既然有了这些结论,那么我们就可以对于每一次操作,枚举一遍 \(t\),最终求得某一情况下期望的逆序对数。

得到这个我们就能轻松地算出最终答案。因为一共有 \(2^q\) 种情况,所以我们用刚刚得到的再乘上 \(2^q\) 就是最终答案。

需要注意的是,计算过程中的除法要写成逆元。

时间复杂度: \(O(n^2 + qn)\)。

代码

//https://www.luogu.com.cn/problem/AT_agc030_d AT_agc030_d [AGC030D] Inversion Sum
#include <bits/stdc++.h>
#define M 3005
#define int long long
#define mod 1000000007

using namespace std;

inline int read() {
    int x = 0, s = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-')
            s = -s;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }
    return x * s;
}

void write(int x) {
    if(x < 0)  {
        x = ~(x - 1);
        putchar('-');
    }
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + 48);
}

int n, q, a[M], dp[M][M], ans;
const int inv2 = 500000004;

inline int  quick_pow(int base, int P) {
    int ji = 1;
    while(P) {
        if(P & 1)
            ji = (ji * base) % mod;
        P >>= 1;
        base = (base * base) % mod;
    }
    return ji;
}

signed main() {
    n = read();
    q = read();
    for(int i = 1; i <= n; ++ i)
        a[i] = read();
    for(int i = 1; i <= n; ++ i)
        for(int j = 1; j <= n; ++ j)
            if(a[i] > a[j])
                dp[i][j] = 1;
    for(int i = 1; i <= q; ++ i) {
        int x = read(), y = read();
        if(x == y)
            continue;
        for(int j = 1; j <= n; ++ j) {
            if(j != x && j != y) {
                dp[y][j] = dp[x][j] = (dp[x][j] + dp[y][j]) % mod * inv2 % mod;
                dp[j][y] = dp[j][x] = (dp[j][x] + dp[j][y]) % mod * inv2 % mod;
            }
        }
        dp[x][y] = dp[y][x] = (dp[x][y] + dp[y][x]) % mod * inv2 % mod;
    }
    for(int i = 1; i <= n; ++ i)
        for(int j = i + 1; j <= n; ++ j)
            ans = (ans + dp[i][j]) % mod;
    ans = ans * quick_pow(2, q) % mod;
    write(ans);
}

后记

放在最后的双倍经验

标签:Inversion,ch,int,题解,位置,base,agc030,dp
From: https://www.cnblogs.com/Meteor-streaking/p/17660265.html

相关文章

  • [ABC317G] Rearranging 题解
    取自我的洛谷博客:https://www.luogu.com.cn/blog/SunnyYuan/solution-at-abc317-g借鉴了官方题解思路。思路首先我们要建立一个二分图。对于输入的\(a_{i,j}\),我们可以连接左侧的\(i\)和右侧的\(a_{i,j}\)。比如样例\(1\):注意:左边的\(1,2,3\)和右边的\(1,2......
  • 「TAOI-2」Ciallo~(∠・ω< )⌒★ 题解
    「TAOI-2」Ciallo~(∠・ω<)⌒★题解不难发现,答案可以分成两种:整段的中间删一点,两端凑一起的考虑分开计算贡献。如果\(s\)中存在子串等于\(t\),那么自然,可以删左边的任何一段,或者右边的任何一段。不妨设子串开始的位置为\(i\),于是其贡献为\((1+2+\cdots+i......
  • YACS 2023年8月月赛 甲组 T2 直线整点 题解
    简单题,先二分出直线上$x$最小的点使得这个点在矩形内。然后不断跳,直到遇到整点。(实际上要用扩欧,但初测能过于是就偷个懒没写) 接着不断跳直到不符合条件。先$\sqrt{V}$个跳一下,跳完后再一个一个跳就不用写二分了多好。代码:#include<iostream>#defineintlonglongusi......
  • UVA908[Re-connecting Computer Sites]题解
    原题1.题意分析题意就是给你很多组数,对于每组数,有三组小数据。第一组小数据先输入一个n表示顶点数,然后再输入n-1条边表示初始边数。其它组小数据先输入一个数k,表示增加的边的数量,然后再输入k条边,表示增加的边。在输入第二组小数据时,要先把边清空,重新输入,但是边的数量不变。2.做......
  • 题解:城市
    题目链接你说得对,但是不如换根。换根是由原先的树形DP简单变换而来,故事发生在这道叫做《城市》的题目中,在这里你妄图求解每个点到树中其它所有节点的距离,即\(f_i=\sum_{j=1}^ndis_{i\toj}\)。可以一次dfs求解出\(f_{root}\),然后我们发现走过一条边\((u,v,w)\)会使......
  • LGR-156-Div.3 题解
    LGR-156-Div.3题解洛谷网校8月普及组月赛I&MXOIRound1&飞熊杯#2第一次AK一个比赛!而且排名这么靠前!!!T1宝箱题目链接思路注意到答案有两种情况。1.从原点走到\(a\),再从\(a\)走到\(b\),2.从原点走到\(b\),再从\(b\)走到\(a\)。取一个最小值即可。代码int......
  • react-pdf在部分iOS手机上加载pdf失败问题解决
    最近项目快结束了,测试提了一个bug,iOS手机上加载pdf一直在转圈,加载不出来内容。看到这个bug,在电脑上和安卓手机上没有问题,iOS手机中打开确实又问题,初步确定为app问题。我们的项目是集成在客户的app里的,可能是app内的WebView和Safari有一些差异导致的问题。首先直接在iOS手机上用a......
  • 力扣-2. 两数相加(C++题解)
    题目链接:https://leetcode.cn/problems/add-two-numbers/description/给你两个 非空的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之......
  • 关于自建Rustdesk 远程桌面服务器的公网访问:无法连接中继服务器的问题解决方法
    自建服务器位于内网时,内网客户端ID/中继的地址通常写成内网IP,外网客户端一般会用公网IP进行端口映射,但这样设置出现外网客户端无法连接中继服务器,但内网客户端使用正常的现象。经过半天的排查分析,当内网和外网填写的自定义服务器地址时,rust服务器无法识别出需要使用nat包的地址,默......
  • P1848 Bookshelf G 题解
    这是本蒟蒻写的第一篇题解(写不好请指出)很明显他是一道dp题,因为第i本书放哪里只跟前i-1本树的放法有关系。我们可以是定义f[i][j]表示放了i本书,最后一层书架是以第j本书开始的。那么有动态转移方程:\(f[i][i]=min(f[i-1][j])+hi,w[j]+...+w[i-1]<=L\)\(f[i][j]=f[i-1][j]+max(0......