题意: 初始时 a=[0,1]a = [0,1]a=[0,1],每次更新将 aaa 逆序对的数量 kkk 插入其中,求最终长度为 nnn 的序列的数量。
以下均设当前的序列逆序对的数量为 kkk。
首先需要通过观察得出一个性质,当序列第一次出现 k>1k > 1k>1 的时候,若将 kkk 插在中间的位置,会使得 k′>kk' > kk′>k,在末尾插入则 k′=kk' = kk′=k。设 fif_ifi 表示长度为 iii 且当前第一次出现 k>1k > 1k>1 时,变为长度为 nnn 的序列的数量。剩下还有 n−in - in−i 次操作,可以将 kkk 均插入末尾,或者在末尾插入若干个以后再将 kkk 插入中间的 iii 个位置之一,因此可以得到以下转移:
fi=i×∑j=0n−i−1fi+j+1+1 f_i = i \times \sum \limits_{j = 0}^{n - i - 1} f_{i + j + 1} + 1 fi=i×j=0∑n−i−1fi+j+1+1
现在我们讨论整一个序列均为 k≤1k \le 1k≤1 的情况。显然 k=0k = 0k=0 时就只有 00⋯0⏟n−11\underbrace{00 \cdots 0}_{n - 1}1n−100⋯01 这一种。而 k=1k = 1k=1 时应为 00⋯0⏟x01011⋯1⏟n−x−3\underbrace{00 \cdots 0}_{x}010\underbrace{11\cdots1}_{n - x - 3}x00⋯0010n−x−311⋯1,其中 x∈[0,n−3]x \in [0,n - 3]x∈[0,n−3],故有 n−2n - 2n−2 种。所以说,k≤1k \le 1k≤1 时,共有 n−1n - 1n−1 种。
最后考虑 k≤1k \le 1k≤1 转移至 k>1k > 1k>1 的情况,显然只有 k=1k = 1k=1 时才有可能。因此对于 00⋯0⏟x01011⋯1⏟n−x−3\underbrace{00 \cdots 0}_{x}010\underbrace{11\cdots1}_{n - x - 3}x00⋯0010n−x−311⋯1 类型的串,我们需要找到第一个 111 所出现的位置。不难发现第一次出现 k>1k > 1k>1 的序列时至少由长度为 333 的 010010010 转移而来,共会产生 222 个长度为 444 且 k>1k > 1k>1 的串 1010,01101010,01101010,0110。一般化的,若在长度为 iii 时第一次出现 k>1k > 1k>1,则这个长度为 iii 的串共会有 ∑j=2i−1=i(i−3)2\sum \limits_{j = 2}^{i - 1} = \frac{i(i - 3)}{2}j=2∑i−1=2i(i−3) 种情况。举个例子,若 i=5i = 5i=5,则可能由 0010,01010010,01010010,0101 转移出 555 种情况。
综上所述,最后的答案就是 n−1+∑i=4ni(i−3)2fin - 1 + \sum \limits _{i = 4}^n \frac{i(i - 3)}{2} f_in−1+i=4∑n2i(i−3)fi。直接后缀和进行处理即可,时间复杂度 O(n)O(n)O(n)。
参考代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 1e6 + 5;
const int MOD = 998244353;
const ll inv_2 = 499122177;
inline int read ();
int t,n;ll sum,ans,dp[MAX];
int main ()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
t = read ();
while (t--)
{
n = read ();
sum = ans = 0;
for (int i = n;i >= 4;--i)
{
dp[i] = (i * sum % MOD + 1) % MOD;
sum = (sum + dp[i]) % MOD;
ans = ans + 1ll * i * (i - 3) % MOD * inv_2 % MOD * dp[i] % MOD;
}
ans = (ans + n - 1) % MOD;
printf ("%lld\n",ans);
}
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}
991

被折叠的 条评论
为什么被折叠?



