【AtCoder】【思維】【置換】Rabbit Exercise

題意:

有n只兔子,i號兔子開始的時候在a[i]號位置。每一輪操作都將若干只兔子依次進行操作:
加入操作的是b[i]號兔子,就將b[i]號兔子移動到關於b[i]-1號兔子現在所在的位置對稱的地方,或者是關於b[i]+1號兔子現在所在的位置對稱的地方,兩者是等概率的。現在給出每一輪操作的兔子編號及順序,要你求k輪之後每隻兔子的位置的期望。保證操作的兔子編號爲2~n-1。

數據範圍:

1<=n,每一輪的操作數量<=100000
1<=k<=10^18

思路:

看見k這麼大,肯定第一反應是有某種週期。
然後來看單獨的一輪操作,是一個簡單的求解期望的問題。因爲選擇b[i]-1號兔子和b[i]+1號兔子是等概率的,那麼當前這隻兔子的期望位置也就是確定的,也就是2a[b[i]1]a[b[i]]+2a[b[i]+1]a[b[i]]2=a[b[i]+1]+a[b[i]1]a[b[i]]\dfrac{2a[b[i]-1]-a[b[i]]+2a[b[i]+1]-a[b[i]]}{2}=a[b[i]+1]+a[b[i]-1]-a[b[i]]。那麼對於單輪的操作來說,就變得簡單了,就是按順序將每個兔子的位置變爲上面所說的值。
那麼考慮有多輪的情況。參考了網上的題解之後,原來是一個很妙的做法,考試的時候我當然沒有想到╮(╯﹏╰)╭
觀察改變之前的序列與查分之後的序列的差分數組。
之前:a[1],a[2],a[3] -> 差分數組:a[1],a[2]-a[1],a[3]-a[1]
之後:a[1],a[1]+a[3]-a[2],a[3] -> 差分數組:a[1],a[3]-a[2],a[2]-a[1]
神奇的事情發生了!!我們發現差分數組中,竟然是兩個位置,也就是i和i+1對換了位置!!
那麼到了這裏,也就不難發現這就是置換了。將每一個循環求出來,然後對於每一個循環,假設循環長度爲T,那麼讓k%=T,然後講這個循環涉及到的所有的位置的答案都求出來,然後就做到O(n)。

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 100000
using namespace std;
typedef long long LL;
vector<int> turns[MAXN+5];//用vector來記錄每一個循環
LL k;
int n,m,tcnt=0,chs[MAXN+5],id[MAXN+5],id2[MAXN+5];
//id存的是單次操作之後的狀態,id2存的是k次操作之後的操作
LL x[MAXN+5],a[MAXN+5];
LL a2[MAXN+5],x2[MAXN+5];
//a2是之後的差分序列,x2是之後的兔子位置
bool vis[MAXN+5];
int main()
{
//	freopen("rabbit.in","r",stdin);
//	freopen("rabbit.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&x[i]),id[i]=i;
	for(int i=1;i<=n;i++)
		a[i]=x[i]-x[i-1];
	scanf("%d %lld",&m,&k);
	for(int i=1;i<=m;i++)
		scanf("%d",&chs[i]),swap(id[chs[i]],id[chs[i]+1]);
	for(int i=1;i<=n;i++)
	{
		int p=i;
		if(vis[p]==true)
			continue;
		tcnt++;
		while(vis[p]==false)
		{
			vis[p]=true;
			turns[tcnt].push_back(p);
			p=id[p];
		}
	}
	for(int i=1;i<=tcnt;i++)
	{
		int T=(int)turns[i].size();//對於每一個循環單獨計算
		int pos=k%T;
		//處理出開頭位置所對應的最終位置,然後向後挪到來求出這個循環裏的其他元素所對應的答案
		for(int j=0,p=pos;j<(int)turns[i].size();j++,p=(p+1)%T)
			id2[turns[i][j]]=turns[i][p];
	}
	for(int i=1;i<=n;i++)
		a2[i]=a[id2[i]];
	for(int i=1;i<=n;i++)
		x2[i]=x2[i-1]+a2[i];
	for(int i=1;i<=n;i++)
		printf("%lld.0\n",x2[i]);//因爲題目要求,強行加一個.0
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章