题解 B. Torch - 2022 ICPC Jinan Site

传送门

好不容易(几乎)单开了一个非数学的金牌题,结果中期卡太久,机时不够了......

至少现场的通过人数来看是金牌题


【大意】

有两个人按每秒 \(1\) 个单位的速度依次行走。

第一个人从 \(0\) 时刻开始,每走 \(a_2\) 秒需要休息 \(b_2\) 秒。第二个人从 \(0\) 时刻开始,每走 \(a_2\) 秒需要休息 \(b_2\) 秒。

初始时刻,第二个人在第一个人身后 \(1\) 个单位,每秒钟第一个人先走,第二个人后走。

第二个人无法穿过第一个人。

\(n\) 次询问,每次询问经过时间 \(q_i\) 后,第二个人走过的距离。


【分析】

由于第一个人的行动状态是很好描述的,经过时间 \(T\) 后,走过的距离为 \(\displaystyle \lfloor{T\over a_1+b_1}\rfloor\cdot a_1+\min(T\bmod (a_1+b_1), a_1)\) ;因此,如果我们能求出经过时间 \(T\) 后,两人走过的距离差,第二个人走过的距离显然也很好求解了。

考虑以第二个人作为参考系,则等价于第一个人运动的周期是 \(\text{lcm}(a_1+b_1, a_2+b_2)\) ,每秒的运动都是 \(-1,0,1\) 的数组;即一个 \(a_1\)\(1\) 拼上 \(b_1\)\(0\) 为周期的数组,减去一个 \(a_2\)\(1\) 拼上 \(b_2\)\(0\) 为周期的数组。

我们称减去后,得到的周期数组为 \(d_i\) ,则第 \(T\) 秒两人之间的距离,是一个初始为 \(0\) 的项,每次加上 \(d_i\) 后取 \(\max\) 得到的结果。

考虑到 \(d_i\) 数组必然是若干段都是 \(-1, 0, 1\) 的数组拼接而成,因此,我们可以将相同的段压缩为 \(\left<val,len\right>\) 的二元组。可以证明,这样的二元组是 \(O(a+b)\) 级别,且可以 \(O(a+b)\) 求出。

于是,问题化为给定二元组表示的周期数组 \(d_i\) ,现在 \(n\) 询问 \(q_i\) 次求和后取 \(\max\) 得到的结果


由于 \(q_i\leq 10^{16}\) ,暴力模拟答案复杂度显然错误。

我们考虑模拟的过程,当 \(q_i\leq \text{lcm}(a_1+b_1, a_2+b_2)\) 时,我们显然可以通过前缀和的方法模拟出所有位置的结果。查询的时候我们只需要二分第一个小于等于询问位置的前缀和位,然后加上尾巴的贡献即可。

但是当 \(q_i>\text{lcm}(a_1+b_1, a_2+b_2)\) 时,我们并不是重复上述的模拟过程,因为在第一轮模拟时,初始值为 \(0\) ;而后续的模拟时,初始值可能会超过 \(0\)

仔细思考一下,其实后续的情况仅有两种,以下进行解释为什么只有两种:

我们称在模拟过程中,一旦模拟到某一步时,答案降为 \(0\) ;则称这一步为模拟过程的零点。那么,显然存在一个性质:当第二轮模拟时,出现零点,则这个零点也是第一轮模拟的零点。(显然,因为第一轮的初始值不会高于第二轮的初始值)

这个性质表示,如果第二轮出现零点,则从此开始,第二轮后续的结果都将和第一轮的结果一致。因此,第二轮结束后,第三轮的初始值是第二轮的输出值,其恰好为第一轮的输出值,即第二轮的初始值;进一步推导,可以证明,接下来所有轮的结果,均与第二轮结果一致。

对于该情况,我们在询问 \(q_i>\text{lcm}(a_1+b_1, a_2+b_2)\) 时,直接放缩到 \(\text{lcm}(a_1+b_1, a_2+b_2)\) 范围内,然后在第二轮模拟的结果内像第一轮那样查询即可。

而如果第二轮模拟时不出现零点,则其输出值必然高于第一轮的输出值;因此第三轮的初始值高于第二轮初始值,必然也不会产生零点;进一步推导可知,后续轮的模拟过程中均不会出现零点。

因此,从第二轮开始,每一轮模拟的输出结果都是上一轮模拟的输出结果加上 \(\displaystyle \sum_i d_i\) ;中间部分的运行结果和第二轮模拟的结果,相比各自的初始值都是一致的。

因此,我们同样像第一种情况那样二分查询后,加上 \(\displaystyle \lfloor{T\over \text{lcm}(a_1+b_1, a_2+b_2)}\rfloor-1\) 倍的 \(\displaystyle \sum_i d_i\) 即可。

综上,我们只需要在 \(d_i\) 数组上用前缀和的方式模拟两轮,然后每次二分查询即可。复杂度为模拟的 \(O(a+b)\) 加上查询的 \(O(n\log(a+b))\)


【代码】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
#define fi first
#define se second
const int MAXN=6e6+10;
const ll inf=1e18;
ll a1, b1, a2, b2;
int n;
vector<pii> v;
vector<pii> dis1, dis2;
ll val1, val2, sumit, L;
inline ll dist(ll q) {
	int idx=-1;
	ll res=0;
	if(q<=L) {
		idx=upper_bound(dis1.begin(), dis1.end(), pii(q, inf)) - dis1.begin() - 1;
		res=dis1[idx].se;
		q-=dis1[idx].fi;
	}
	else {
		ll tmp=q/L-1;
		q-=(tmp+1)*L;
		if(val1!=val2)
			res=sumit*tmp;
		idx=upper_bound(dis2.begin(), dis2.end(), pii(q, inf)) - dis2.begin() - 1;
		res += dis2[idx].se;
		q-=dis2[idx].fi;
	}
	return max(0ll, res+q*v[idx].fi);
}
inline ll simulate(vector<pii> &v, vector<pii> &dis, ll ini_val) {
	dis.clear();
	dis.reserve(v.size());
	ll sum_len=0;
	for(auto e : v) {
		dis.push_back(pii(sum_len, ini_val));
		sum_len+=e.se;
		ini_val = max(ini_val+e.fi*e.se, 0ll);
	}
	return ini_val;
}
inline void init() {
	v.clear();
	ll pos1=0, pos2=0;
	int flag1=1, flag2=1;
	do {
		ll dis1=(flag1?a1:a1+b1)-pos1;
		ll dis2=(flag2?a2:a2+b2)-pos2;
		ll dis=min(dis1, dis2);
		v.push_back(pii(flag1-flag2, dis));
		pos1=(pos1+dis)%(a1+b1);
		pos2=(pos2+dis)%(a2+b2);
		flag1=(pos1<a1);
		flag2=(pos2<a2);
	}while(pos1||pos2);
	L=(a1+b1)/__gcd(a1+b1, a2+b2)*(a2+b2);
}
inline void work() {
	cin>>a1>>b1>>a2>>b2>>n;
	init();
	val1=simulate(v, dis1, 0);
	val2=simulate(v, dis2, val1);
	sumit=0;
	for(auto e : v) sumit+=e.fi*e.se;
	
	ll q;
	for(int i=1; i<=n; ++i) {
		cin>>q;
		cout<<q/(a1+b1)*a1+min(q%(a1+b1), a1)-dist(q)<<"\n";
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T; cin>>T;
	while(T--)
		work();
	return 0;
}

【补充】

简单证明一下,为什么按相同的值合并后,长度是 \(O(a+b)\) 级别的。

我们考虑将 \(a_1\)\(1\) 拼上 \(b_1\)\(0\) 为周期的序列倍长至 \(\displaystyle \text{lcm}(a_1+b_1, a_2+b_2)\) 。由于其被重复了 \(\displaystyle {\text{lcm}(a_1+b_1, a_2+b_2)\over a_1+b_1}\) 次,每一次内部最多有一次的 \(0/1\) 分界,相邻两次之间最多也只有一次 \(0/1\) 分界。因此,分界点的级别是 \(O(a_2+b_2)\) 的。

同理,将 \(a_2\)\(1\) 拼上 \(b_2\)\(0\) 为周期的序列倍长后,分界点的级别同样是 \(O(a_1+b_1)\) 的。

我们将两个序列对减后,最坏情况下即为所有分界点恰好错开,新序列的分界点级别仍然是 \(O(a+b)\) 的。

由于分界点中间的一段值都必然相同,因此将相同的段合并后,长度即为 \(O(a+b)\) 的。

posted @ 2022-11-28 17:42  JustinRochester  阅读(43)  评论(0编辑  收藏  举报