题解:CF2039E Shohag Loves Inversions

题意: 初始时 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 - ini 次操作,可以将 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=0ni1fi+j+1+1

现在我们讨论整一个序列均为 k≤1k \le 1k1 的情况。显然 k=0k = 0k=0 时就只有 00⋯0⏟n−11\underbrace{00 \cdots 0}_{n - 1}1n10001 这一种。而 k=1k = 1k=1 时应为 00⋯0⏟x01011⋯1⏟n−x−3\underbrace{00 \cdots 0}_{x}010\underbrace{11\cdots1}_{n - x - 3}x000010nx3111,其中 x∈[0,n−3]x \in [0,n - 3]x[0,n3],故有 n−2n - 2n2 种。所以说,k≤1k \le 1k1 时,共有 n−1n - 1n1 种。

最后考虑 k≤1k \le 1k1 转移至 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}x000010nx3111 类型的串,我们需要找到第一个 111 所出现的位置。不难发现第一次出现 k>1k > 1k>1 的序列时至少由长度为 333010010010 转移而来,共会产生 222 个长度为 444k>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=2i1=2i(i3) 种情况。举个例子,若 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_in1+i=4n2i(i3)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值