计数 luogu 4223 期望逆序对

本文针对洛谷P4223题目提供了一种解决方案,通过将期望问题转化为计数问题,并利用矩阵快速幂求解。文章详细介绍了状态划分及转移矩阵的构建过程,最终采用树状数组维护答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.luogu.org/problemnew/show/P4223

期望乘以\(\binom {n}{2}^k\)变成了计数问题

我们考虑每一组数\((A, B)\)产生的贡献CCCCCACCCCBCCCC

分7组考虑\((A, B)\)\(k\)次操作之后去哪里了

\((A, B)\; (A, C)\;(B,A)\;(B,C)\;(C,A)\;(C,B)\;(C,C)\)

可以列出一个\(7 \times 7\)的矩阵表

矩阵快速幂后表示转移\(k\)次之后的系数(有点恶心

有一个结论就是CCCACCCBCCC

对于比如\((A, C)\)这个状态

\(B\)到达\(A\)左边和到达\(B\)右边的方案数是和\(C\)的个数成正比的

数学归纳法可证

因此推出式子可以发现\(i\)\(j>i\)的贡献是一样的

树状数组维护即可

复杂度\(\mathcal O(n\log n)\)

#include <bits/stdc++.h>
#define int long long
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\n"
#define type(x) __typeof((x).begin())
#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); ++ it)
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
  x = 0;char c = getchar(); bool f = 0;
  for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
  if(f) x = -x;
}
const int N = 5e5 + 233;
namespace {
  const int mo = 1e9 + 7;
  inline int add(int u, int v) {
    if((u += v) >= mo) u -= mo;
    return u;
  }
  inline int sub(int u, int v) {
    if((u -= v) < 0) u += mo;
    return u;
  }
  inline int mul(int u, int v) {
    return u * v % mo;
  }
  inline int pw(int a, int k, int mo) {
    int ans = 1;
    for(; k; k >>= 1, a = mul(a, a))
      if(k & 1) ans = mul(ans, a);
    return ans;
  }
}
struct Mar {
  int m[7][7];
  Mar() {
    memset(m, 0, sizeof m);
  }
  inline void E(void) {
    for(int i = 0; i < 7; i ++)
      m[i][i] = 1;
  }
}f, ans;
int a[N];
int cnt = 0, n, K;
Mar operator * (Mar a, Mar b) {
  Mar c;
  for(int i = 0; i < 7; i ++)
    for(int j = 0; j < 7; j ++) {
      int t = 0;
      for(int k = 0; k < 7; k ++)
        t = add(t, mul(a.m[i][k], b.m[k][j]));
      c.m[i][j] = t;
    }
  return c;
}

inline void I(int a, int b, int c, int d, int e, int g, int h) {
  f.m[cnt][0] = a; f.m[cnt][1] = b; f.m[cnt][2] = c;
  f.m[cnt][3] = d; f.m[cnt][4] = e; f.m[cnt][5] = g;
  f.m[cnt][6] = h;
  cnt ++;
}

inline void Matrix_Init(void) {
  int t = (n - 2) * (n - 3) / 2 % mo;
  I(t, n - 2, 1, 0, 0, n - 2, 0);
  I(1, t + n - 3, 0, 1, 1, 0, n - 3);
  I(1, 0, t, n - 2, n - 2, 0, 0);
  I(0, 1, 1, t + n - 3, 0, 1, n - 3);
  I(0, 1, 1, 0, t + n - 3, 1, n - 3);
  I(1, 0, 0, 1, 1, t + n - 3, n - 3);
  I(0, 1, 0, 1, 1, 1, t + 2 * (n - 4) + 1);
}

inline Mar mf(Mar a, int k) {
  Mar ans; ans.E();
  for(; k; k >>= 1, a = a * a)
    if(k & 1) ans = ans * a;
  return ans;
}

struct Bit {
  int tr[N], n;
  inline void init(void) {
    memset(tr, 0, sizeof tr);
    n = ::n;
  }
  inline void A(int u, int val) {
    for(; u <= n; u += u & -u)
      tr[u] = add(tr[u], val);
  }
  inline int Q(int u) {
    int ans = 0;
    for(; u >= 1; u -= u & -u)
      ans = add(ans, tr[u]);
    return ans;
  }
}x, y, z;

inline void doit(void) {
  x.init(); y.init(); z.init();
  int p = pw(n - 2, mo - 2, mo);
  int inv2 = pw(2, mo - 2, mo);
  int res = mul(n * (n - 1) / 2 % mo, ans.m[0][6]);
  res = mul(res, inv2);
  for(int j = 1; j <= n; j ++) {
    int t;
    int sm = x.Q(a[j] - 1);
    int la = j - 1 - sm;
    int p1 = add(mul(ans.m[0][3], mul(n - j, p)), mul(ans.m[0][5], mul(j - 2, p)));
    int p2 = add(mul(ans.m[0][3], mul(j - 2, p)), mul(ans.m[0][5], mul(n - j, p)));
    res = add(res, add(mul(la, p1), mul(sm, p2)));

    res = add(res, add(mul(la, ans.m[0][0]),
                       mul(sm, ans.m[0][2])));
    res = add(res, add(sub(y.Q(n), y.Q(a[j])), z.Q(a[j] - 1)));
    
    x.A(a[j], 1);

    t = add(mul(ans.m[0][1], mul(n - j - 1, p)),
            mul(ans.m[0][4], mul(j - 1, p)));
    y.A(a[j], t);
    
    t = add(mul(ans.m[0][1], mul(j - 1, p)),
            mul(ans.m[0][4], mul(n - j - 1, p)));
    z.A(a[j], t);
  }
  cout << res << "\n";
}

main(void) {
  read(n); read(K);
  fo(i, n) read(a[i]);
  Matrix_Init();
  ans = mf(f, K);
  doit();
}

转载于:https://www.cnblogs.com/foreverpiano/p/luggu4223.html

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int N = 1010; 7 8 int n, m, mod; 9 long long ans, y[N], f[N], g[N], p[N], s[N]; 10 11 long long qmi(long long a, int b, int p) { 12 long long res = 1; 13 while (b) { 14 if (b & 1) 15 res = res * a % p; 16 a = a * a % p; 17 b >>= 1; 18 } 19 return res; 20 } 21 22 long long solve(int n, int k) { 23 memset(y, 0, sizeof(y)); 24 memset(f, 0, sizeof(f)); 25 memset(g, 0, sizeof(g)); 26 memset(p, 0, sizeof(p)); 27 memset(s, 0, sizeof(s)); LUOGU SCP-S 2025 第一轮 C++语言试题 第 6 页,共 17 页 28 long long ans = 0; 29 for (int i = 1; i <= k + 2; i++) 30 y[i] = (y[i - 1] + qmi(i, k, mod)) % mod; 31 f[0] = g[0] = 1; 32 for (int i = 1; i <= k + 2; i++) 33 f[i] = f[i - 1] * i % mod; 34 for (int i = 1; i <= k + 2; i++) 35 g[i] = -g[i - 1] * i % mod; 36 p[0] = s[k + 3] = 1; 37 for (int i = 1; i <= k + 2; i++) 38 p[i] = p[i - 1] * (n - i) % mod; 39 for (int i = k + 2; i >= 1; i--) 40 s[i] = s[i + 1] * (n - i) % mod; 41 for (int i = 1; i <= k + 2; i++) { 42 long long t1 = p[i - 1] * s[i + 1] % mod; 43 long long t2 = f[i - 1] * g[abs(k - i + 2)] % mod; 44 (ans += y[i] * t1 % mod * qmi(t2, mod - 2, mod) % mod) %= mod; 45 ans = (ans + mod) % mod; 46 } 47 return ans; 48 } 49 50 int main() { 51 scanf("%d%d%d", &n, &m, &mod); 52 long long res = 0; 53 for (int i = 1; i <= n; i++) { 54 res += (n - i + 1) * qmi(m, n - i, mod) % mod * solve(m, i); 55 res %= mod; 56 } 57 printf("%lld", res); 58 return 0; 59 } 假设输入的 𝒏, 𝒎 是不超过 𝟏𝟎𝟎𝟎 的正整数,mod 是大于 𝟏𝟎 𝟒,但是不超过 𝟏𝟎 𝟗 + 𝟕 的质 数,完成下面的判断题和单选题。 ·判断题 21. qmi 函数实现了快速幂算法。该函数对于任意的不超过 109 + 7 的非负整数 b 和不超 LUOGU SCP-S 2025 第一轮 C++语言试题 第 7 页,共 17 页 过 109 + 7 的正整数 a,p 均能正确返回 𝑎 𝑏 对 𝑝 取模的值。( ) 22. 删除程序的第 23 到 27 行,程序仍然能正常运行,且输出结果不变。( ) 23. 当输入的 𝑚 为 1 时,输出总为 𝑛(𝑛 2 +1)。( ) ·单选题 24. 这份程序的最坏时间复杂度是( )。 A. Θ(𝑛 2 ) B. Θ(𝑛 log 𝑛) C. Θ(𝑛 2 log 𝑛) D. Θ(𝑛 2 log(𝑛 × mod)) 25. 对于以下的输入数据,输出结果为( )。 3 4 10331 A. 1900 B. 9500 C. 820 D. 1650 26. (4 分)这份程序求得了( )的答案对 mod 取模的结果。 A. 对于所有长度为 𝑛 的,值域为 [1, 𝑚] 的整数序列 𝑎,计算 ∑ ∑ max𝑘 𝑗 =𝑖 𝑎𝑘 𝑛 𝑗=𝑖 𝑛 𝑖=1 之和。 B. 对于所有长度为 𝑚 的,值域为 [1, 𝑛] 的整数序列 𝑎,计算 ∑ ∑ max𝑘 𝑗 =𝑖 𝑎𝑘 𝑚 𝑗=𝑖 𝑚 𝑖=1 之和。 C. 对于所有长度为 𝑛 的,值域为 [1, 𝑚] 的整数序列 𝑎,计算 ∑ ∑ min𝑘 𝑗 =𝑖 𝑎𝑘 𝑛 𝑗=𝑖 𝑛 𝑖=1 之和。 D. 对于所有长度为 𝑚 的,值域为 [1, 𝑛] 的整数序列 𝑎,计算 ∑ ∑ min𝑘 𝑗 =𝑖 𝑎𝑘 𝑚 𝑗=𝑖 𝑚 𝑖=1 之和。
最新发布
08-15
<think>首先,用户的问题是关于一个C++代码的分析,但用户提供的引用中并没有具体的C++代码。用户说:"我想分析C++代码的功能并解答相关问题",但在引用的内容中,只有一些算法的时间复杂度讨论和递归方程,没有给出完整的C++代码。用户上一次的问题是关于有向图和无向图的性质判断,而这次突然转向C++代码分析,这似乎不连贯。 回顾用户的消息:"我想分析C++代码的功能并解答相关问题 请问分析给定C++代码的功能,判断其中的函数作用、程序时间复杂度、输出结果以及该程序求解的问题模型。" 但用户没有提供具体的C++代码。在引用中,有提到一些代码片段,比如: - 引用[1]:一个while循环,`void fun (int n) { int i=0; while(i*i*i<=n) i++; }` - 引用[2]:两个for循环的例子 - 引用[4]:一个递归函数findMaxCnt()的时间复杂度分析 - 引用[5]:矩阵链乘法的递归方程 用户可能期望我分析这些引用中的代码。但用户说"给定C++代码",却没有明确指定是哪一个。或许用户指的是引用中的代码片段。 用户的上一次问题是关于图论的,而这次是关于算法分析的,所以可能是一个新问题。用户提到"参考站内引用",并列出了一些引用,所以我应该基于这些引用来回答。 用户要求:"分析给定C++代码的功能,判断其中的函数作用、程序时间复杂度、输出结果以及该程序求解的问题模型。" 在引用中,最完整的代码片段是引用[1]中的`fun`函数和引用[4]中的`findMaxCnt`函数(尽管没有给出完整代码,只有描述)。 引用[1]:`void fun (int n) { int i=0; while(i*i*i<=n) i++; }` 这是一个简单的函数。 引用[4]:提到了`findMaxCnt()`函数,其时间复杂度递推式为T1(n) = 2T1(n/2) + O(n),这类似于归并排序的分治策略。 引用[5]:给出了矩阵链乘法的递归方程,但没有代码。 我应该聚焦在引用[1]和引用[4]上,因为它们有具体的函数描述。 用户还要求生成相关问题,所以我需要确保回答结构清晰。 首先,分析引用[1]中的代码。 **函数作用:** 这个函数计算最大的整数i,使得i的三次方小于或等于n。也就是说,它找到满足i^3 <= n的最大i。 **时间复杂度:** 循环条件是i*i*i <= n。i从0开始递增,直到i^3 > n。i的增长是线性的吗?不,因为i^3在增长,所以i会增加到大约n^{1/3}。因此,循环次数是O(n^{1/3})。 更精确地,i 会增加到满足 i^3 <= n < (i+1)^3,所以 i ≈ ∛n。因此,时间复杂度是O(∛n)。 **输出结果:** 函数没有输出,但最终i的值是满足i^3 <= n的最大整数。函数结束后,i就是立方根的整数部分。 **问题模型:** 这个函数求解的是求整数立方根的问题,即对于给定的n,找到最大的整数i使得i^3 <= n。 现在,引用[4]中的`findMaxCnt()`函数。 引用[4]说:"findMaxCnt()函数的时间复杂度递推式为:T1(n) = 2T1(n/2) + O(n) for n>1, and O(1) for n=1",并推导出T1(n) = O(n log n)。 但函数本身没有给出,只有时间复杂度分析。用户可能期望我推断函数的作用。 从递推式T(n) = 2T(n/2) + O(n),这类似于分治算法,如归并排序,其中问题被分成两个子问题,每个大小为n/2,并在线性时间内合并。 引用[4]还说:"整个算法的时间复杂度为:T(n) = O(n) + O(n log n) + T1(n) + O(1) = O(n log n)",所以`findMaxCnt`可能是算法的一部分。 但具体功能不明确。或许它是在计算某个最大值或计数,类似于求逆序对或最大子数组等。 由于代码不完整,我应该专注于引用[1]的代码,因为它更具体。 用户说"分析给定C++代码的功能",但只给出了片段。或许在上下文中,用户意指这些引用中的代码。 为了提供完整回答,我将分析引用[1]的代码,因为它是完整的函数。 **回答结构:** 1. **函数作用:** 解释代码的功能。 2. **时间复杂度:** 分析并给出大O表示。 3. **输出结果:** 说明函数结束后,变量的状态或隐含输出。 4. **问题模型:** 描述这个函数解决什么问题。 然后,生成相关问题。 对于引用[1]的代码:`void fun (int n) { int i=0; while(i*i*i<=n) i++; }` - **函数作用:** 这个函数计算满足 $i^3 \leq n$ 的最大整数 $i$。它通过递增 $i$ 从0开始,直到 $i^3 > n$。 - **时间复杂度:** 循环次数取决于 $n$。$i$ 从 0 增加到 $\lfloor \sqrt[3]{n} \rfloor$,所以循环次数大约是 $\sqrt[3]{n}$。因此,时间复杂度是 $O(\sqrt[3]{n})$ 或更精确地说 $O(n^{1/3})$。 - **输出结果:** 函数没有返回值或输出语句,但函数结束后,变量 `i` 存储了满足 $i^3 \leq n$ 的最大整数值。例如,如果 n=8,i 会增加到 2,因为 2^3=8<=8,然后 i++ 变成 3,3^3=27>8,循环停止,i=3。但注意,当 i=3 时,i^3=27>8,所以最大 i 满足 i^3 <= n 是 i=2,但函数结束时 i=3。这有点问题。 在循环中,当 i*i*i <= n 时,i 递增。所以当 i=0, 0<=n 总是真(假设 n>=0),i 变成 1。然后 1<=n,i 变成 2,等等。当 i=k 时,k^3 <= n,i 变成 k+1。然后检查 (k+1)^3 <= n?如果真,继续;否则停止。所以当循环停止时,i 是满足 i^3 > n 的最小整数。因此,满足 i^3 <= n 的最大整数是 i-1。 例如,n=8:i 从 0 开始。 - i=0, 0<=8 真,i 变成 1 - i=1, 1<=8 真,i 变成 2 - i=2, 8<=8 真,i 变成 3 - i=3, 27>8 假,循环停止。i=3。 但最大整数使得立方小于等于8是2,而不是3。所以函数实际上计算了满足 i^3 <= n 的最小整数上界,但结果 i 是立方根的上取整整数值?不,因为对于 n=8,立方根是2,i 变成 3。 实际上,函数结束时 i = floor(∛n) + 1。 因为 floor(∛n) 是最大整数 m 使得 m^3 <= n,那么 i = m + 1。 所以函数隐含地找到了 floor(∛n) + 1。 - **问题模型:** 这个函数解决了计算整数立方根的上界问题,或者更具体地说,它找到了最小的整数 i 使得 i^3 > n,这等价于 floor(∛n) + 1。 现在,关于时间复杂度,在引用[1]中,问题本身是关于这个函数的时间复杂度,选项是 O(log2n), O(n), O(n log2n), O(n^2)。但根据分析,它是 O(n^{1/3}),不在选项中。选项包括 O(log2n) 等,但 O(n^{1/3}) 不是对数或线性。 在用户消息中,引用[1] 是:"6.有以下算法,其时间复杂度为( )。 void fun (int n) { int i=0; while(i*i*i<=n) i++; } [^1]。选项:A. O(log2n) B. O(n) C. O(n log2n) D. O(n^2)" 但 O(n^{1/3}) 不是这些选项。或许我需要计算精确的阶。 i 从 0 开始增加,每次 i 增加 1,直到 i^3 > n。i 的最大值 k 满足 k^3 ≈ n, 所以 k ≈ n^{1/3}。因此,循环次数是 Θ(n^{1/3})。 但选项中没有一个直接匹配。O(log n) 太小,O(n) 太大,O(n log n) 更大,O(n^2) 更大。或许用户期望选择最接近的,但没有。 或许对于大O,O(n^{1/3}) 可以视为 sub-linear,但选项中没有。 在上下文中,或许我需要参考标准答案。 但用户现在要求分析功能等,所以我应该基于分析。 或许用户想让我分析这个代码作为示例。 转向引用[4],但那里没有完整代码。 或许用户意指引用[4]中的 findMaxCnt 函数。 引用[4]:"findMaxCnt()函数的时间复杂度递推式为:T1(n) = 2T1(n/2) + O(n) for n>1",并推导为 O(n log n)。 从递推式,它类似于分治算法,如归并排序或求最大子数组。 函数名 "findMaxCnt" 暗示它可能是在找最大计数,比如在数组中找到出现次数最多的元素,但使用分治。 但具体不确定。 或许结合引用,用户想讨论时间复杂度。 用户说"该程序求解的问题模型",所以需要推断。 为了实际回答,我将以引用[1]的代码为例进行分析。 **回答:** ### 分析 C++ 代码功能 考虑引用[1]中提供的代码片段: ```cpp void fun (int n) { int i=0; while(i*i*i <= n) i++; } ``` #### 1. 函数作用 此函数计算满足 $i^3 > n$ 的最小整数 $i$。它通过初始化 $i = 0$,并在 $i^3 \leq n$ 的条件下递增 $i$ 来实现。当循环结束时,$i$ 的值是第一个使 $i^3 > n$ 的整数[^1]。 #### 2. 时间复杂度 循环的执行次数取决于 $n$。设 $k$ 为循环次数,则 $k$ 是满足 $k^3 \leq n$ 的最大整数加一。由于 $k \approx \sqrt[3]{n}$,因此循环次数为 $\Theta(n^{1/3})$。时间复杂度为 $O(n^{1/3})$[^1]。 #### 3. 输出结果 函数本身没有输出语句,但函数执行后,变量 `i` 存储了满足 $i^3 > n$ 的最小整数值。例如: - 若 $n = 8$,则循环后 $i = 3$(因为 $3^3 = 27 > 8$)。 - 若 $n = 27$,则循环后 $i = 4$(因为 $4^3 = 64 > 27$)。 该值等价于 $\lfloor \sqrt[3]{n} \rfloor + 1$[^1]。 #### 4. 问题模型 此函数求解的问题是:给定整数 $n \geq 0$,找到最小的整数 $i$ 使得 $i^3 > n$。这等价于计算立方根的整数部分上取整,或用于确定整数立方根的上界[^1]。 对于引用[4]中提到的 `findMaxCnt()` 函数,由于代码未提供,基于时间复杂度递推式 $T(n) = 2T(n/2) + O(n)$,它可能是一个分治算法,用于解决如求数组中的最大频率元素、归并排序中的计数问题或类似逆序对计数的问题。时间复杂度为 $O(n \log n)$[^4]。 ### 相关问题 §§相关问题§§ 1. 如何优化计算整数立方根的算法? 2. 分治算法的时间复杂度分析有哪些常见方法? 3. 在C++中,如何实现一个函数来精确计算整数的立方根(向下取整)? 4. 矩阵链乘法问题(如引用[5])的动态规划解法如何工作?</think>### 分析 C++ 代码功能(基于引用[1]的代码) 以下分析针对引用[1]中提供的代码片段: ```cpp void fun(int n) { int i = 0; while (i * i * i <= n) i++; } ``` #### 1. **函数作用** 该函数计算满足 $i^3 > n$ 的最小整数 $i$。 - 通过初始化 $i = 0$,并在条件 $i^3 \leq n$ 成立时递增 $i$。 - 当循环结束时,$i$ 的值是第一个使 $i^3 > n$ 的整数[^1]。 - **等价问题**:计算 $\lfloor \sqrt[3]{n} \rfloor + 1$(立方根整数部分的上界)。 #### 2. **时间复杂度** - **分析**: 设循环次数为 $k$,则 $k$ 满足 $k^3 \approx n$(因循环终止于 $i^3 > n$)。 解得 $k = \Theta(n^{1/3})$,故时间复杂度为 $O(n^{1/3})$[^1][^3]。 - **对比选项**: 在引用[1]的选项中($O(\log_2 n)$, $O(n)$, $O(n \log_2 n)$, $O(n^2)$),均不直接匹配 $O(n^{1/3})$,因其为次线性复杂度。 #### 3. **输出结果** - 函数无显式输出,但执行后变量 `i` 存储结果。示例如下: | 输入 $n$ | 最终 `i` 值 | 解释($i^3 > n$) | |----------|-------------|------------------| | $n=0$ | $i=1$ | $1^3=1 > 0$ | | $n=8$ | $i=3$ | $3^3=27 > 8$ | | $n=27$ | $i=4$ | $4^3=64 > 27$ | - **关键点**:结果 $i$ 是 $\lfloor \sqrt[3]{n} \rfloor + 1$,例如 $n=8$ 时 $\lfloor \sqrt[3]{8} \rfloor = 2$,$i=3$[^1]。 #### 4. **问题模型** - **求解问题**:给定整数 $n \geq 0$,求最小整数 $i$ 使得 $i^3 > n$。 - **应用场景**: - 确定整数立方根的上界(如算法中预先分配内存)。 - 在数值计算中作为迭代初始值(如牛顿法求立方根)[^3]。 --- ### 补充:引用[4]中分治算法的分析 引用[4]提到函数 `findMaxCnt()` 的时间复杂度递推式为: $$ T(n) = \begin{cases} O(1) & \text{if } n = 1 \\ 2T(n/2) + O(n) & \text{if } n > 1 \end{cases} $$ - **函数作用**:基于分治策略(如归并排序),可能用于计算数组中的最大频率元素或逆序对计数。 - **时间复杂度**:通过递推式展开得 $T(n) = O(n \log n)$[^4]。 - **问题模型**:典型的分治问题(如排序、统计或树形结构处理)。 --- ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值