您的位置:首页 > 其它

JZOJ 5598 全排列

2018-03-24 14:34 281 查看
传送门

考场上的思路

参考代码

总结

传送门

考场上的思路

  差一点就对了……样例太坑爹……

  枚举区间的长度 ll,则对于长度 ll 在排列中有 n−l+1n−l+1 个可选位置。考虑对答案的贡献,当我们确定了排列 aa 的区间的元素时,我们可以直接计算排列 bb 对答案的贡献。假设 aa 的区间中的元素确定了,那么 bb 对答案的贡献就是 Cln∗(n−l)!Cnl∗(n−l)!。再考虑 aa 中长度为 ll 的区间有多少个(要满足逆序对需求),不难发现 EE 的范围是假的,最大也只有 n(n−1)2n(n−1)2,因此可以设 fi,jfi,j 表示长度为 ii 且逆序数小于等于 jjj 的排列个数,可以 O(n3)O(n3) 转移。考试时可以先考虑逆序数恰好为 jj 的排列,最后来求前缀和,发现转移正好可以利用前缀和转移,然后就 O(n3)O(n3) 了。

  因此对于 aa 区间长度为 ll 的区间个数为 Cln×(n−l)!×fl,min(E,n(n−1)2)Cnl×(n−l)!×fl,min(E,n(n−1)2),把它和之前 bb 对答案的贡献乘起来就好了(因为互相独立)。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
INT_PUT a = 0;
bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-')
{
positive = false;
ch = getchar();
}
while (std::isdigit(ch))
{
a = a * 10 - (ch - '0');
ch = getchar();
}
if (positive) a = -a;
return a;
}
void printOut(INT_PUT x)
{
char buffer[20];
int length = 0;
if (x < 0) putchar('-');
else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
putchar('\n');
}

const int mod = int(1e9) + 7;
const int maxn = 505;
const int maxto = 500;
const int maxe = maxto * (maxto - 1) / 2;

int f[maxn][maxn * maxn / 2];
int C[maxn][maxn];
int fac[maxn];
void init()
{
C[0][0] = 1;
for (register int i = 1; i <= maxto; i++)
{
C[i][0] = 1;
for (register int j = 1; j <= i; j++)
{
int temp;
C[i][j] = (temp = C[i - 1][j - 1] + C[i - 1][j]) >= mod ? temp - mod : temp;
}
}

for (register int i = 0; i <= maxe; i++)
f[1][i] = 1;
for (register int i = 2; i <= maxto; i++)
{
for (register int j = 0, to = i * (i - 1) / 2; j <= to; j++)
{
f[i][j] = ((LL)f[i][j] + f[i - 1][j] - (j - i >= 0 ? f[i - 1][j - i] : 0) + mod) % mod; // 直接利用前缀和转移
}
for (register int j = 1; j <= maxe; j++)
{
register int temp;
f[i][j] = (temp = f[i][j] + f[i][j - 1]) >= mod ? temp - mod : temp;
}
}
fac[0] = 1;
for (int i = 1; i <= maxto; i++)
fac[i] = (LL)fac[i - 1] * i % mod;
}

int n, E;

void run()
{
init();

int T = readIn();
while (T--)
{
n = readIn();
E = std::min(readIn(), n * (n - 1) / 2);
LL ans = 0;
for (register int i = 1; i <= n; i++)
ans = (ans + (LL)fac[n - i] * fac[n - i] % mod * (n - i + 1) % mod * f[i][E] % mod * C
[i] % mod * C
[i] % mod) % mod;
printOut(ans);
}
}

int main()
{
#ifndef LOCAL
freopen("perm.in", "r", stdin);
freopen("perm.out", "w", stdout);
#endif
run();
return 0;
}


总结

  考试时就是忘了还要考虑区间外的元素对答案的贡献(×(n−l)!×(n−l)!),然后就爆零了。考试时也没有发现可以直接用前缀和转移,还以为是个卷积要用任意模数 NTT QAQ。

  以后要对拍了……不打暴力还是不稳啊,T1 暴力错了也是蛮拼的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: