题意:
一共 个点,每个点都有自己的初始位置 ,一共有 次操作,每次操作给出一个 ,则 ,如果 , ;如果 , 。现将 次操作称为一组操作,将该组操作执行 次,求最后每个点的值。
思路:
该题的表象是数值变化,每操作一个点,就有一个点的数值发生变化。但我们要将一组操作执行 次,规模非常巨大,因此如果只是数值发生变化,我们难以求出如此大规模之后的答案,所以很明显一定要找到某种规律用更简单的方式来表达这种数值的变化。
一番挣扎过后…悄咪咪地看了题解…的确是想不出来,然后发现是通过差分来体现这种规律,具体规律如下。
用
表示差分数组,
。操作一次
位置,发生如下变化。
由上述三条式子可以发现,操作
个点的值,相当于在差分数组中将
位置和
位置的数进行了一次交换。由此我们得到了一次交换后的数值。
那如何得到 次交换的数值呢?这里就涉及到了此题第二个巧妙的点,倍增!用倍增来快速求出大量重复性操作之后的答案。 表示位置 操作 组后对应的位置,则 ,由此即可进行倍增。
总结:
此题最重要的两个关键点。
- 通过差分数组将 “数值变化” 转换为 “交换变化”
- 通过倍增将大量重复性操作的复杂度变为 ,不由得感叹倍增的神奇
代码:
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const db EPS = 1e-9;
using namespace std;
void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int n,m,a[N],pre[N][70];
ll x[N],k,v[N],ans[N];
void init(){
for(ll j = 1; (1ll<<j) <= k; j++)
for(int i = 0; i <= n; i++)
pre[i][j] = pre[pre[i][j-1]][j-1];
}
int query(int pos,ll t){
if(t == 0) return pos;
for(ll j = 62; j >= 0; j--)
if((1ll<<j) <= t) return query(pre[pos][j],t-(1ll<<j));
}
int main()
{
scanf("%d",&n);
rep(i,1,n) scanf("%lld",&x[i]);
x[n+1] = x[n-1]; x[0] = x[2];
v[0] = x[0];
rep(i,1,n+1) v[i] = x[i]-x[i-1];
rep(i,0,n+1) pre[i][0] = i;
scanf("%d%lld",&m,&k);
rep(i,1,m){
scanf("%d",&a[i]);
swap(pre[a[i]][0],pre[a[i]+1][0]);
}
init();
rep(i,0,n+1) ans[i] = v[i];
rep(i,1,n) ans[i] = v[query(i,k)];
rep(i,1,n){
ans[i] = ans[i-1]+ans[i];
printf("%lld.0\n",ans[i]);
}
return 0;
}