洛谷P1521 求逆序对 题解

本文介绍了一种计算从1到n的所有排列中恰好包含m对逆序对的方案数量的方法。通过递推思想,文章给出了两种算法实现:一种是n^4级别的暴力解法,另一种是优化至n^3级别的高效解法。

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

题意:

  求1到n的全排列中有m对逆序对的方案数。

思路:

  1.f[i][j]表示1到i的全排列中有j对逆序对的方案数。
  2.显然,1到i的全排列最多有(i-1)*i/2对逆序对,而对于f[i][j]来说,新加入一个数i+1,产生的新的逆序对数与插入的位置有关(数目为插入的数的位置之后的数的数目),于是n^4暴力就新鲜出炉了。
  3.换一个角度来说,当i>j的时候,我们枚举i的全排列的第一位的数字,如果是1,那么就要求剩下i-1个数中有j个全排列,如果是2,要求剩下i-1个数中有i-2个
全排列,依次类推,得到了第一个方程:f[i][j]=sum{f[i-1][0],f[i-1][1],f[i-1][2].....f[i-1][j]}
    当i<=j的时候,由于i的全排列中最大的数字是i,所以把i放到第一位上,由第一位最多能能产生i-1个逆序对,把1放到第一位上能产生0个逆序对,所以i-1-1+1=i-1,这时的f[i][j]就要由f[i-1][j]开始算上他自己,总共i-1项的和。因此:f[i][j]=sum{f[i-1][j],f[i-1][j-1],f[i-1][j-2].....f[i-1][j-i+1]}

反思:

  我只会暴力,优化只能理性愉悦一下了,好菜啊……

代码:

  n^4暴力:

 

 1 #include<cstdio>
 2 int n,m,i,j,k,f[201][10000];
 3 
 4 int main()
 5 {
 6     scanf("%d%d",&n,&m);
 7     f[1][0]=1;
 8     for (i=1;i<n;++i)
 9         for (j=0;j<=(i-1)*i>>1;++j)
10             for (k=0;k<=i;++k)
11                 if ((f[i+1][j+i-k]+=f[i][j])>=10000) f[i+1][j+i-k]-=10000;
12     printf("%d\n",f[n][m]);
13     return 0;
14 }

  n^3:

 1 #include<cstdio>
 2 int n,m,i,j,f[101][5000];
 3 
 4 int main()
 5 {
 6     scanf("%d%d",&n,&m);
 7     f[1][0]=1;
 8     for (i=2;i<=n;++i)
 9     {
10         for (j=0;j<i;++j) f[i][j]=(f[i][j-1]+f[i-1][j])%10000;//因为f[i,j]里面统计的是类似于前缀和的一个东西,f[i][j]只比f[i][j-1]多了f[i-1][j]这一项
11         for (;j<=(i-1)*i>>2;++j) f[i][j]=(f[i][j-1]+f[i-1][j]-f[i-1][j-i])%10000;//这时求和的项数确定了,就是i-1项,于是f[i,j]比f[i][j-1]多了f[i-1][j]这一项,少了f[i-1][j-i]这一项
12         for (;j<=(i-1)*i>>1;++j) f[i][j]=f[i][((i-1)*i>>1)-j];//因为数对总数为i*(i-1)/2,而一个数对要么是逆序对要么是顺序对,因此f[i][j]是关于f[i][i*(i-1)/4]呈现中心对称的
13     }
14     printf("%d\n",(f[n][m]+10000)%10000);
15     return 0;
16 }

 

转载于:https://www.cnblogs.com/HHshy/p/7189664.html

<think>好的,我需要帮助用户查找P1601的题解或相关信息。首先,用户提供的引用中有提到引用[3]是关于P1601、P1303和P1009的高精度题目,特别是A+B Problem。因此,我应该重点参考引用[3]的内容。 接下来,我需要确认题目P1601的具体要。根据引用[3],题目涉及高精度运算,输入的两个数a和b不超过10的500次方,说明需要处理大数相加的问题,普通的整型变量无法存储这么大的数值,必须用字符串处理每一位数字。 然后,我需要解析用户提供的引用内容。引用[3]提到分两行输入,输出格式需要正确的结果,这意味着在编程实现时需要注意输入输出的处理方式。此外,引用[3]还提到了高精度的算法,这通常涉及到逐位相加并处理进位的问题。 接下来,我需要整理解题思路。高精度加法的基本步骤是将两个字符串形式的数字逆序,逐位相加,处理进位,最后将结果逆序输出。需要特别注意前导零的情况,以及最高位的进位处理。 然后,我需要编写示例代码。根据常见的C++高精度加法实现方法,可以写出相应的函数。代码需要包括输入处理、字符串逆序、逐位相加、进位处理和结果逆序输出等步骤。 最后,我需要生成相关问题,这些问题应该围绕高精度算法平台的使用以及其他相关题目展开,帮助用户进一步学习。</think>### P1601 高精度加法解析 P1601 是典型的高精度加法题目,要处理长度不超过 $10^{500}$ 的非负整数相加[^3]。由于常规数据类型无法存储如此大的数值,需通过字符串逐位运算实现。 #### 解题步骤 1. **输入处理** 读取两行字符串 $a$ 和 $b$,如: ```plaintext 1145141919810 2000000000001 ``` 2. **逆序对齐** 将字符串反转,使个位对齐: $$a_{rev} = "019181945411" \quad b_{rev} = "1000000000002"$$ 3. **逐位相加** 从低位到高位依次相加,记录进位: ```python carry = 0 for i in range(max_len): digit_a = int(a_rev[i]) if i < len_a else 0 digit_b = int(b_rev[i]) if i < len_b else 0 total = digit_a + digit_b + carry result.append(total % 10) carry = total // 10 ``` 4. **处理最高位进位** 若循环结束后 $carry > 0$,需补上最高位: $$3145141919811$$ #### 示例代码(C++) ```cpp #include <iostream> #include <algorithm> using namespace std; string add(string a, string b) { reverse(a.begin(), a.end()); reverse(b.begin(), b.end()); string res; int carry = 0; for (int i = 0; i < max(a.size(), b.size()); ++i) { int na = (i < a.size()) ? (a[i] - '0') : 0; int nb = (i < b.size()) ? (b[i] - '0') : 0; int sum = na + nb + carry; res.push_back(sum % 10 + '0'); carry = sum / 10; } if (carry) res.push_back('1'); reverse(res.begin(), res.end()); return res; } int main() { string a, b; cin >> a >> b; cout << add(a, b); return 0; } ``` #### 注意事项 - **前导零处理**:输入可能含前导零,但题目未明确要处理,可保留原始格式 - **时间复杂度**:$O(n)$ 时间复杂度,$n$ 为数字长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值