agc_044e Random Pawn

agc_044e Random Pawn

https://atcoder.jp/contests/agc044/tasks/agc044_e

Snipaste_2020-06-21_08-15-11.png

Tutorial

http://auoj.net/download.php?type=solution-pdf&id=413

https://atcoder.jp/contests/agc044/submissions/14156414

假如卒子移动至 \(A_i\) 最大的位置,那么一定会直接结束,所以可以断环为链

发现我们需要决策的就是终止点位置的集合,也就是走到哪些位置就直接停止

首先考虑,若所有 \(B_i=0\) 时的答案.

\(P_i\) 表示从第 \(i\) 个位置出发的期望收益.

那么对于相邻的两个终止点 \(u,v\)

\[\begin{cases} P_u=A_u \\ P_v=A_v \\ P_i=\dfrac {P_{i-1}+P_{i+1}}2 & i \in (u,v) \end{cases} \]

这是一个经典模型,可以得到

\[P_i = \dfrac {(i-u)A_v+(v-i)A_u}{v-u} (i \in [u,v]) \]

发现这是一个等差数列的形式,可以看作梯形下方的面积.

那么发现,如果要最大化决策点连成的图形下方的面积大小,所选择的就是上凸壳.

那么求 \((i,A_i)\) 的上凸壳集合即可.

\(B_i \not= 0\) .

此时 \(P_i = \dfrac {P_{i-1}+P_{i+1}}2-B_i\) .我们想要将其转化为与之前相似的等差数列的形式.

考虑设 \(P'_i = P_i-C_i\) ,考虑构造数列 \(C\) 使得上述性质成立.

则有

\[\begin{cases} P'_u=A_u-C_u \\ P'_v=A_v-C_v \\ P'_i = \dfrac {P_{i-1}+P_{i+1}}2-B_i -C_i = \dfrac {P'_{i-1}+P'_{i+1}}2-B_i-C_i+\dfrac {C_{i-1}+C_{i+1}}2 & i \in (u,v) \end{cases} \]

那么 \(C\) 数列需要满足 \(B_i = \dfrac {C_{i-1}+C_{i+1}}2-C_i\) .

可以令 \(C_1=C_2=0,C_{i+1}=2(B_i+C_i)-C_{i-1}\)

那么对于 \((A-C,P'_i)\) 求解后,很容易还原出原问题的答案.

复杂度 \(O(n)\)

Code

#include <algorithm>
#include <cstdio>
#include <iostream>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char nc() {
	return getchar();
	static char buf[100000],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x) {
	x=0; int f=1,ch=nc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
	x*=f; 
}
typedef long long ll;
const int maxn=200000+5;
int n; ll a[maxn]; int b[maxn];
ll c[maxn];
int sta[maxn],top;
inline bool invalid(int u,int v,int w) {
	return (a[v]-a[u])*(w-v)<=(a[w]-a[v])*(v-u); 
}
void rebuild() {
	int k=max_element(a+1,a+n+1)-a;
	rotate(a+1,a+k,a+n+1);
	rotate(b+1,b+k,b+n+1);
	a[++n]=a[1],b[n]=b[1];
}
void init() {
	for(int i=2;i<n;++i) c[i+1]=2*(b[i]+c[i])-c[i-1];
	for(int i=1;i<=n;++i) a[i]-=c[i];
}
double sol() {
	rebuild();
	init();
	for(int i=1;i<=n;++i) {
		while(top>1&&invalid(sta[top-1],sta[top],i)) --top;
		sta[++top]=i;
	}
	ll an=0;
	for(int i=1;i<top;++i) {
		int d=sta[i+1]-sta[i];
		an+=a[sta[i]]*(d+1)+a[sta[i+1]]*(d-1);
	}
	for(int i=1;i<n;++i) an+=c[i]*2;
	return an/(2.0*(n-1));
}
int main() {
	read(n);
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=1;i<=n;++i) read(b[i]);
	printf("%.12lf\n",sol());
	return 0;
}
posted @ 2020-06-21 08:32  LJZ_C  阅读(308)  评论(1编辑  收藏  举报