P12503 「ROI 2025 Day1」索契游乐园 题解

P12503 「ROI 2025 Day1」索契游乐园 题解


知识点

二分,三分,贪心,数学。

分析

首先可以知道我们可以把折返的贡献分为两边独立的贡献,只要一边移动的贡献设为 \(t\),另一边设为 \(2t\) 即可。

那么将坐标 \(<x_0\)\(>x_0\) 的分开到两个数组中,重新将距离设为 \(|x-x_0|\)\(x=x_0\) 的随便处理,反正毫无贡献)。

尝试列出贡献表达式,设 \(c\) 表示走了几个 \(d\)

\[ct+ \sum_{x_i\le cd}\min((x_i\bmod d)^2,(x_i - x_i \bmod d)^2) + \sum_{x_i > cd} (x_i-cd)^2 \\ \]

那么 \(ct\) 的部分我们可以手算,\(\sum_{x_i\le cd}\min((x_i\bmod d)^2,(x_i - x_i \bmod d)^2)\) 我们可以前缀和预处理,\(\sum_{x_i > cd} (x_i-cd)^2\) 我们可以拆式子手算:

\[ct+ sum_{i_0} + suf2_{i_0+1} - 2c \cdot d \cdot suf_{i_0+1} + (n-i_0)(cd)^2 \\ \]

记号约定:

  • \(i_0\) 是满足 \(x_i\le cd\) 的最大 \(i\)
  • \(sum_i\) 表示 \(\sum_{j=1}^i\min((x_i\bmod d)^2,(x_i - x_i \bmod d)^2)\)
  • \(suf_i\) 表示 \(\sum_{j=i+1}^nx_j\)
  • \(suf2_i\) 表示 \(\sum_{j=i+1}^nx_j^2\)

然后可以三分求解(如何证明呢?不知道,略略略),复杂度 \(O(m(\log_2{n}+\log_{\frac{3}{2}}{V}))\),但是,吼吼吼,过不了怎么办?

注意到表达式是一个二次函数的形式(除了 \(i_0=n\) 的时候):

\[(n-i_0)d^2 \times c^2 + (t - 2\cdot d \cdot suf_{i_0+1}) \times c + (sum_{i_0} + suf2_{i_0+1}) \\ \]

那么每一段预处理后,可以直接用公式求出大概的最值位置,然后找到最近的 \(d\) 的倍数即可,那么三分部分复杂度变为 \(O(\log_{\frac{3}{2}}{n})\)

最后再用二分代替整数域三分,复杂度 \(O(m\log_2{n})\),可能还要一堆乱搞卡常才可以过……

发现其实点的位置随 \(t\) 递减是单调递增的(如何证明呢?还是不知道,略略略略略),所以我们直接离线询问尺取做到 求解部分 只有 \(O(n+m)\),就可以过了!!!

最后的复杂度:如果你愿意写基排,那么就是 \(O(n+m)\)(大常数警告),不然也只有排序的 \(O(n\log_2{n}+m\log_2{m})\),常数很小。

代码

#define Int __int128
#define Fi first
#define Se second
#define Pii pair<int,int>
constexpr int N(3e5+10),Qr(6e5+10);

int n,m,D,Q,X,tot;
int ta[N],a[N];
ll sum[N],suf[N];
ll ans[Qr][2][2];
Int suf2[N];
Pii pos[N];
struct Query {
	int t,idx;
	friend bool operator <(const Query &a,const Query &b) { return a.t>b.t; }
} Que[Qr<<1];

int pre(int x) { return x/D*D; }

int nxt(int x) { return (x+D-1)/D*D; }

ll sq(ll x) { return x*x; }

Int f(ll A,Int B,int x) { return (Int)A*x*x+B*x; }

Int calc(int t,int p) {
	if(p>tot)return (ll)(a[m]+D-1)/D*t+sum[m];
	const int l(pos[p-1].Fi+1),r(pos[p].Fi),x(pos[p].Se);
	const ll A((ll)D*D*(m-x+1));
	const Int B(t-(Int)2*D*suf[x]);
	const int mid(-B/(2*A));
	Int ans(0);
	if(l<=mid&&mid<=r) {
		ans=f(A,B,mid);
		if(mid<r)tomin(ans,f(A,B,mid+1));
	} else ans=f(A,B,r<mid?r:l);
	return ans+sum[x-1]+suf2[x];
}

void Solve(const bool ty) {
	if(!m)return;
	tot=0,pos[0].Fi=-1,suf[m+1]=0,suf2[m+1]=0;
	FOR(i,1,m) {
		sum[i]=sum[i-1]+min(sq(a[i]-pre(a[i])),sq(a[i]-nxt(a[i])));
		if(i<=1||pre(a[i])!=pre(a[i-1]))pos[++tot]= {a[i]/D,i};
	}
	DOR(i,m,1)suf[i]=suf[i+1]+a[i],suf2[i]=suf2[i+1]+sq(a[i]);
	int it(1);
	FOR(i,1,Q) {
		while(it<=tot&&calc(Que[i].t,it)>calc(Que[i].t,it+1))++it;
		const int &idx(Que[i].idx);
		(idx>0?ans[idx][ty][0]:ans[-idx][ty][1])=calc(Que[i].t,it);
	}
}

signed main() {
	/*DE("Input & Query");*/
	I(n);
	FOR(i,1,n)I(ta[i]);
	I(X,D,Q);
	FOR(i,1,Q) {
		int t;
		I(t),Que[(i<<1)-1]= {t,i},Que[i<<1]= {t<<1,-i};
	}
	Q<<=1;
	/*DE("Solve");*/
	sort(ta+1,ta+n+1),sort(Que+1,Que+Q+1);
	// left
	m=0;
	DOR(i,lower_bound(ta+1,ta+n+1,X)-ta-1,1)a[++m]=X-ta[i];
	Solve(0);
	// right
	m=0;
	FOR(i,upper_bound(ta+1,ta+n+1,X)-ta,n)a[++m]=ta[i]-X;
	Solve(1);
	/*DE("Output");*/
	Q>>=1;
	FOR(i,1,Q)O(min(ans[i][0][0]+ans[i][1][1],ans[i][0][1]+ans[i][1][0]),'\n');
	return 0;
}

posted @ 2025-09-07 10:54  Add_Catalyst  阅读(12)  评论(0)    收藏  举报