题解 AT2164 【Rabbit Exercise】

ezoixx130

2019-09-11 20:37:06

Solution

考虑跳第 $i$ 个点的时候,如果往左跳会跳到 $a_{i-1}-(a_{i}-a_{i-1})$ 即 $2a_{i-1}-a_i$ 的位置上,如果往右跳会跳到 $a_{i+1}+(a_{i+1}-a_i)$ 即 $2a_{i+1}-a_i$ 的位置上。 所以对 $i$ 个点进行操作的话,期望位置会变成 $\large \frac{2a_{i-1}-a_i+2a_{i+1}-a_i}{2}$ 即 $\large a_{i-1}-a_i+a_{i+1}$。 但是这种操作不好维护,因为你要做 $10^{18}$ 轮这样的操作,暴力处理显然不是正解。 如果将期望序列差分,考虑对 $i$ 进行操作的时候,设原本第 $i-1,i,i+1$ 号点的位置期望分别为 $a,b,c$,那么差分序列为 $b-a,c-b$。对 $i$ 号点进行完操作以后,期望序列变为 $a,a-b+c,c$,差分序列为 $c-b,b-a$。也就是说,对一个点进行操作等价于在期望的差分序列上交换两个数。 那么这样就好维护多了,因为这样的一轮操作相当于对差分序列进行一个置换。要做 $10^{18}$ 轮这样的操作,对置换进行快速幂即可。 时间复杂度:$O(n\log k)$ 代码: ```cpp #include <bits/stdc++.h> using namespace std; #define MAXN 100010 int n,m,a[MAXN],ans[MAXN],res[MAXN],tmp[MAXN]; long long k; int main() { scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",a+i); for(int i=1;i<n;++i)ans[i]=i; scanf("%d%lld",&m,&k); for(int i=1;i<=m;++i) { int x; scanf("%d",&x); swap(ans[x],ans[x-1]); } for(int i=1;i<n;++i)res[i]=i; while(k) { if(k&1) for(int i=1;i<n;++i)res[i]=ans[res[i]]; for(int i=1;i<n;++i)tmp[i]=ans[ans[i]]; for(int i=1;i<n;++i)ans[i]=tmp[i]; k>>=1; } long long sum=a[1]; for(int i=1;i<=n;++i) { printf("%lld.0\n",sum); sum+=a[res[i]+1]-a[res[i]]; } } ```