【AtCoder-2164 C】Rabbit Exercise AtCoder【差分找規律+倍增】

題意:

一共 nn 個點,每個點都有自己的初始位置 xix_i,一共有 MM 次操作,每次操作給出一個 pospos,則 a[pos]=a[pos+1]+a[pos1]2a[pos]a[pos]=a[pos+1]+a[pos-1]-2*a[pos],如果 pos=1pos=1a[1]=2a[2]a[1]a[1]=2*a[2]-a[1];如果 pos=npos=na[n]=2a[n1]a[n]a[n]=2*a[n-1]-a[n]。現將 MM 次操作稱爲一組操作,將該組操作執行 KK 次,求最後每個點的值。(1n,M105,1K1018)(1\leq n,M\leq 10^5,1\leq K\leq 10^{18})


思路:

該題的表象是數值變化,每操作一個點,就有一個點的數值發生變化。但我們要將一組操作執行 101810^{18} 次,規模非常巨大,因此如果只是數值發生變化,我們難以求出如此大規模之後的答案,所以很明顯一定要找到某種規律用更簡單的方式來表達這種數值的變化。

一番掙扎過後…悄咪咪地看了題解…的確是想不出來,然後發現是通過差分來體現這種規律,具體規律如下。

ff 表示差分數組,f[i]=a[i]a[i1]f[i]=a[i]-a[i-1]。操作一次 ii 位置,發生如下變化。
a[i]=a[i1]+a[i+1]a[i]f[i]=a[i]a[i1]=a[i+1]a[i]f[i+1]=a[i+1]a[i]=a[i]a[i1] a[i]'=a[i-1]+a[i+1]-a[i] \\ f[i]'=a[i]'-a[i-1]=a[i+1]-a[i] \\ f[i+1]'=a[i+1]-a[i]'=a[i]-a[i-1]
由上述三條式子可以發現,操作 11 個點的值,相當於在差分數組中將 ii 位置和 i+1i+1 位置的數進行了一次交換。由此我們得到了一次交換後的數值。

那如何得到 kk 次交換的數值呢?這裏就涉及到了此題第二個巧妙的點,倍增!用倍增來快速求出大量重複性操作之後的答案。pre[i][j]pre[i][j] 表示位置 ii 操作 2j2^j 組後對應的位置,則 pre[i][j]=pre[pre[i][j1]][j1]pre[i][j]=pre[pre[i][j-1]][j-1],由此即可進行倍增。


總結:

此題最重要的兩個關鍵點。

  1. 通過差分數組將 “數值變化” 轉換爲 “交換變化”
  2. 通過倍增將大量重複性操作的複雜度變爲 loglog,不由得感嘆倍增的神奇

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章