题解:AT_arc218_e [ARC218E] Reverse and Reverse
dyc2022
·
·
题解
更好的阅读体验
终于读懂了官解。
我们考虑将 (p_1, p_2, \cdots, p_n) 变成 (p_i, p_{i-1}, \cdots, p_1, p_n, p_{n-1}, \cdots p_{i+1}) 这个操作是在干什么。容易发现,这个操作可以等价为,先 reverse 这个序列,再循环右移 i 位;而这个操作同样可以看作,先循环右移 n-i 位,然后 reverse 这个序列。
下文中 \text{rev. = reverse, rot. = rotate}。那么我们先用第一种表示方法,把操作序列写下来,比如 \text{rev.} \to \text{rot.}\to \text{rev.} \to \text{rot.} \to \cdots。由上一段的内容容易发现,后一组操作可以变成 \text{rot.}\to \text{rev.},并且相邻两次循环移位操作可以合并。这意味着反转操作一定可以提到最后面。因此进行 k 次的题目所给操作,可以看成是先进行 k 次循环移位操作(每次偏移量是 [1, n-1] 中的一个数),再将序列反转 k 次。
根据反转之前的逆序对数推算反转之后的逆序对数是容易的,接下来讨论如何处理 k 次移位后逆序对数的问题。
我们假设 v_i 表示有多少种方案,使得 k 次移位操作之后,排列相比于初始状态的偏移量为 i(其中 i \isin [0, n-1])。那么我们断言,对于 v 数组,有
\begin{gather}
v_1 = v_2 = \cdots v_{n-1} = X \nonumber \\
v_0 = X + (-1)^k \nonumber
\end{gather}
证明
由一次移位的偏移量是 1 \sim n-1,写出 v 数组的 OGF。因为系数对 n 取模,因此 \bmod (1 - x^n) 的意思就是,视 x^n = x^0。
\begin{align}
&\space \space \space \space \space \left(x_{n-1} + x_{n-2} + \cdots + x\right) ^ k \bmod (1 - x^n) \nonumber \\
&= \left[\left(x^{n-1} + x^{n-2} + \cdots + 1\right) - 1\right]^k \bmod (1 - x^n) \nonumber \\
&= \sum_{i=0}^k (-1)^{k-i} {k \choose i} \left(x^{n-1} + x^{n-2} + \cdots + 1\right)^i \bmod (1-x^n) \nonumber \\
&= (-1)^k + \sum_{i=1}^k (-1)^{k-i} {k \choose i} \left(x^{n-1} + x^{n-2} + \cdots + 1\right)^i \bmod (1-x^n)
\end{align}
考虑
\begin{align}
&\space \space \space \space \space \left(x^{n-1} + x^{n-2} + \cdots + 1\right)^2\bmod (1-x^n) \nonumber \\
&= \sum_{i=0}^{n-1} \sum_{j=0}^{n-1}x^{(i + j) \bmod n}\bmod (1-x^n) \nonumber
\end{align}
方程 (i + j) \bmod n = k 在 [0, n-1] 的解数,对于 k \isin [0, n-1] 应当都是 n,因为对于每个 i 都有且仅有一个 j 满足这个式子。所以上式
= n\left(x^{n-1} + x^{n-2} + \cdots + 1\right)\bmod (1-x^n)
带回 (1) 可以得到
= (-1)^k + \sum_{i=1}^k (-1)^{k-i} {k \choose i} n^{i-1}\left(x^{n-1} + x^{n-2} + \cdots + 1\right) \bmod (1-x^n)
由这个式子,我们就能注意到,x^1 \sim x^{n-1} 的系数都是相等的,而 x^0 系数相差 (-1)^k。
命题得证。
有了这个结论,我们容易由 \sum\limits_{i=0}^{n-1} v_i = (n-1)^k 求解 X,进而得到 v 数组的值。
那么我们假设 I_i 表示序列循环移位 i 次后,有多少个逆序对。那么
\text{answer} = \sum_{i=0}^{n-1} v_iI_i = X\sum_{i=0}^{n-1}I_i + (-1)^k I_0
由于是邻项交换,I_0 的变化是容易维护的。考虑如何求出 \sum_{i = 0}^{n-1}I_i。
我们可以枚举数字,看看这两个数字有多少种偏移量能产生逆序对。考虑 p 的逆排列 p^{-1}。对于 x, y (x<y),能使 x, y 产生逆序对的偏移量个数显然是 (p^{-1}_y - p^{-1}_x) \bmod n。
所求即为
\sum_{i=1}^n \sum_{j=1}^n (p^{-1}_j - p^{-1}_i) \bmod n
发现 (p^{-1}_j - p^{-1}_i) \bmod n 等于 p^{-1}_j - p^{-1}_i 或者 p^{-1}_j - p^{-1}_i+n,而总共需要 +n 的个数是 p^{-1} 这个序列中的逆序对数。由于每次在 p^{-1} 中交换的都是值域相邻的数,因此这个逆序对数也是好维护的。
接下来我们只需要知道如何维护
\sum_{i=1}^n \sum_{j=1}^n p^{-1}_j - p^{-1}_i
一次假设交换 k, k+1,其中 p^{-1}_x = k, p^{-1}_y = k+1,不妨假设 x < y。看一下那些 i, j 的 p^{-1}_j - p^{-1}_i 会有变化:
***
那么这道题就做完了。我们在一开始的时候用树状数组分别求出 $p, p^{-1}$ 的逆序对数;查询的时候需要使用快速幂求 $(n-1)^k$ 的值。因此时间复杂度是 $O(n \log n + q \log k)$。
代码有注释。
```cpp
#include<bits/stdc++.h>
#define endl '\n'
#define N 200006
#define MOD 998244353
using namespace std;
inline void add(int &x,int y) {x+=y,x-=x>=MOD?MOD:0;}
inline void dec(int &x,int y) {x+=MOD-y,x-=x>=MOD?MOD:0;}
int n,q,a[N],ia[N],tot,inv,inv_ia;
inline int qpow(int x,int y)
{
int ret=1;
for(;y;y>>=1,x=1ll*x*x%MOD)if(y&1)ret=1ll*ret*x%MOD;
return ret;
}
struct BIT {
int tree[N];
void upd(int k,int x) {for(;k<=n;k+=k&-k)add(tree[k],x);}
int query(int k)
{
int ret=0; for(;k;k-=k&-k)add(ret,tree[k]);
return ret;
}
int query(int l,int r)
{
int ret=query(r);
return dec(ret,query(l-1)),ret;
}
} T1,T2;
//tot= \sum_{1<=i<j<=n} (ia[j] - ia[i]) % n
inline void update(int p)
{
int x=a[p],y=a[p+1];
if(x<y)add(inv,1); else dec(inv,1);
if(x<y)
{
add(inv_ia,1);
//ia[x]=p, ia[y]=p+1
// <=> ia[x]++, ia[y]--
dec(tot,2); //i=x, j=y, tot-=2
dec(tot,n-x-1); //i=x, j!=y, tot-=(n-x-1)
add(tot,x-1); //j=x
add(tot,n-y); //i=y
dec(tot,y-2); //i!=x, j=y, tot-=(y-2)
} else {
dec(inv_ia,1);
//ia[y]=p+1, ia[x]=p
// <=> ia[x]--, ia[y]++
add(tot,2); //i=y, j=x, tot+=2
add(tot,n-y-1); //i=y, j!=x, tot+=(n-y-1)
dec(tot,y-1); //j=y
dec(tot,n-x); //i=x
add(tot,x-2); //i!=y, j=x, tot+=(x-2)
}
swap(ia[x],ia[y]);
swap(a[p],a[p+1]);
}
inline int query(int k)
{
int v;
//k%2==0: (t[0], t[1], ..., t[n-1]) = (v+1, v, v, v, v, v)
//k%2==1: (t[0], t[1], ..., t[n-1]) = (v-1, v, v, v, v, v)
if(k&1)v=1ll*(qpow(n-1,k)+1)*qpow(n,MOD-2)%MOD;
else v=1ll*(qpow(n-1,k)+MOD-1)*qpow(n,MOD-2)%MOD;
int ret=1ll*v*((tot+1ll*inv_ia*n%MOD)%MOD)%MOD;
k&1?dec(ret,inv):add(ret,inv);
if(k&1)ret=((1ll*n*(n-1)/2)%MOD*qpow(n-1,k)%MOD+MOD-ret)%MOD;
return ret;
}
main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),ia[a[i]]=i;
for(int i=1;i<=n;i++)
{
add(tot,1ll*ia[i]*(i-1)%MOD);
dec(tot,1ll*ia[i]*(n-i)%MOD);
add(inv,T1.query(a[i]+1,n)),T1.upd(a[i],1);
add(inv_ia,T2.query(ia[i]+1,n)),T2.upd(ia[i],1);
}
while(q--)
{
int p,k; scanf("%d%d",&p,&k),update(p);
printf("%d\n",query(k));
}
return 0;
}
```