【题解】 AtCoder-agc006C Rabbit Exercise

Problem

AtCoder & bzoj

题意:数轴上有 n n n个点(初始坐标均为整数),编号为 1 1 1~ n n n。给出 m m m个操作。
每个操作会选定点 a a a,然后随机在点 a − 1 a-1 a1和点 a + 1 a+1 a+1中选一个,将点 a a a以选中的点为中心做对称,将这 m m m个操作按顺序执行 k k k遍( 1 1 1~ m m m完整执行一次算 1 1 1遍),求最终每个点的位置的期望值

Solution

不难发现根据期望的线性型,在 a − 1 a-1 a1 a + 1 a+1 a+1之间随机选一个进行对称操作的期望等价于在 a − 1 a-1 a1 a + 1 a+1 a+1的中点处进行对称

则我们发现,对于点 B B B在点 A A A C C C之间,若 A A A B B B距离为 a a a,若 B B B C C C距离为 b b b,则对称后的位置 B ′ B' B A A A距离为 b b b,与 C C C距离为 a a a(如下图)

发现如果我们用一个差分数组 d i = a i + 1 − a i d_i=a_{i+1}-a_i di=ai+1ai存下 a i a_i ai数组的话,对称操作相当于交换 d i , d i + 1 d_i,d_{i+1} di,di+1

发现进行一轮操作后,整个序列会成为若干个对换环(一个对换环相当于将整个环旋转一格再重新赋值),而进行 k k k次操作相当于将所有环旋转 k k k

发现如果整个环的大小为 c c c,则环旋转 k k k次和旋转 k   m o d   c k\bmod c kmodc次是等价的,则复杂度与 k k k无关,整体复杂度 O ( n + m ) O(n+m) O(n+m)

Code

#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
typedef long long ll;
#define rg register

template <typename _Tp> inline _Tp read(_Tp&x){
	char c11=getchar(),ob=0;x=0;
	while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=101000;
int a[N],vis[N],st[N];
ll b[N],p[N],k;
int n,m,tp;

void init();void work();void print();
int main(){init();work();print();return 0;}

void work(){
	int x;
	for(rg int i=1;i<=m;++i)read(x),swap(a[x],a[x+1]);
	for(rg int i=1;i<=n;++i)if(!vis[i]){
		vis[st[0]=x=i]=tp=1;
		while(!vis[a[x]])
			vis[st[tp++]=x=a[x]]=1;
		int e=k%tp;
		for(rg int j=0;j<tp;++j)
			b[st[j]]=p[st[j+e<tp?j+e:j+e-tp]];
	}
}

void print(){
	ll sm(0ll);
	for(rg int i=1;i<=n;++i)
		printf("%lld\n",sm+=b[i]);
}

void init(){
	read(n);
	for(rg int i=1;i<=n;++i)read(p[i]),a[i]=i;
	for(rg int i=n;i;--i)p[i]-=p[i-1];
	read(m),read(k);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值