校内训练2019-11-08新婚快乐
Posted chhokmah
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了校内训练2019-11-08新婚快乐相关的知识,希望对你有一定的参考价值。
【题目概括】
在一个线性道路上除了起点和终点一共有(n)个点,给出(n+1)个路段的长度(w_i),以及在每一个路口的红绿灯的周期,所有红绿灯的周期是相同的,绿灯持续时间为(g),红灯持续时间为(r)。
车在过路口的时候,如果是红灯就会被拦下来,直到下一个绿灯。
现在给定(m)个车出发的时间(t_i),求出到达终点的最短时间。
【思路要点】
- 对于所有的红绿灯的周期都是相同的,所以我们可以将问题划分为两个子问题:被第一个红绿灯拦住之前、拦住之后。
- 第一个子任务:
- 我们记录前缀和方便计算两两之间的距离,(sum[i]=sum^{i}_{j=1}w_j)
- 那么被第(i)红绿灯拦住的充要条件:((t_s+sum[i])mod (g+r)>g)。(原本在第(g)秒是可以过红绿灯的,但是从零开始标号的时间在第(g)秒就被拦住了)
- 我们用权值线段树维护最小编号,将原式展开后得到了((t_smod (g+r)+sum[i]mod (g+r))mod (g+r))。
- 可以将这个情况分成两种情况讨论,如果(t_sleq g),那么加上(sum[i]mod (g+r))也不可能超过(g),所以这样只有一段答案区间([g-t_s,mod-1-t_s])。
- 否则有两段区间分别为([0,mod-1-t_s])和([mod-(t_s-(g-1))+1,mod-1])。
- 第二个子任务我们可以通过(DP)求解。
- 令(g[i])表示从第(i)个路灯的绿灯开始到终点的最短时间。
- 转移方程为:(g[i]=g[j]+dis(i,j)+(g+r-dis(i,j)mod (g+r)))
- 其中(j)是(i)后第一个被拦住的红绿灯。
- 我们也用类似于上面的做法,从后向前(DP),用权值线段树优化(DP),也就是固定(sum[i]mod (g+r)),然后查找答案区间最小的(j)。(本质就是向后数个数)
- 其实就是两个(sum[i]mod (g+r))和(sum[j]mod (g+r))在线段树上的距离(ge g),转化成严格(>g-1),这样我们就只需要求出我们不需要的区间.
- 如果(sum[i]mod (g+r)-1<(g+r)-1)也就说明不需要的区间是一段连续的区间:([sum[i]mod (g+r),sum[i]mod (g+r)+g-1]),那么查询的答案区间就被分成了两段([sum[i]mod (g+r)+g,mod-1])和([0,sum[i]mod (g+r)-1])。
- 否则不需要的区间就变成了两段,我们查询区间就变成了一段([(sum[i]mod (g+r)+g-1)mod (g+r)+1,(sum[i]mod (g+r)-1])。
- 时间复杂度 (mathcal{O}(m imes log(g+r)))
【代码】
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
template <typename T> void chkmax(T& x, T y) { x = x > y ? x : y; }
template <typename T> void chkmin(T& x, T y) { x = x < y ? x : y; }
const int N = 2e5 + 5;
ll w[N], sum[N], dp[N];
int n, g, r;
ll mod;
ll getDis(int i, int j) {
if (i > j)
return 0;
return sum[j] - sum[i];
}
#define lc (tr[nod].ch[0])
#define rc (tr[nod].ch[1])
struct Node {
int ch[2];
ll val;
} tr[N * 32];
int root, tot = 0;
void pushup(int nod) {
tr[nod].val = INF;
if (lc)
chkmin(tr[nod].val, tr[lc].val);
if (rc)
chkmin(tr[nod].val, tr[rc].val);
}
void insert(int& nod, ll l, ll r, ll k, int v) {
if (!nod)
tr[nod = ++tot].val = INF;
if (l == r) {
tr[nod].val = v;
return;
}
ll mid = (l + r) >> 1;
if (k <= mid)
insert(lc, l, mid, k, v);
else
insert(rc, mid + 1, r, k, v);
pushup(nod);
}
ll query(int nod, ll l, ll r, ll ql, ll qr) {
if (!nod)
return INF;
if (ql <= l && r <= qr)
return tr[nod].val;
ll mid = (l + r) >> 1, res = INF;
if (ql <= mid)
chkmin(res, query(lc, l, mid, ql, qr));
if (qr > mid)
chkmin(res, query(rc, mid + 1, r, ql, qr));
return res;
}
ll queryPos1(ll sumi) {
sumi %= mod;
ll res = INF;
if (sumi + g - 1 < mod - 1) {
ll t1 = query(root, 0, mod - 1, sumi + g, mod - 1);
ll t2 = query(root, 0, mod - 1, 0, sumi - 1);
chkmin(res, t1), chkmin(res, t2);
}
else
chkmin(res, query(root, 0, mod - 1, (sumi + g - 1) % mod + 1, sumi - 1));
return res;
}
ll queryPos2(ll t0) {
t0 %= mod;
ll res = INF;
if (t0 <= g)
chkmin(res, query(root, 0, mod - 1, g - t0, mod - 1 - t0));
else {
chkmin(res, query(root, 0, mod - 1, 0, mod - 1 - t0));
chkmin(res, query(root, 0, mod - 1, mod - (t0 - (g - 1)) + 1, mod - 1));
}
return res;
}
int main() {
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> g >> r;
mod = g + r;
for (int i = 1; i <= n + 1; i++)
cin >> w[i];
sum[0] = 0;
for (int i = 1; i <= n + 1; i++)
sum[i] = sum[i - 1] + w[i];
dp[n + 1] = w[n + 1];
for (int i = n; i >= 1; i--) {
ll pos = queryPos1(sum[i]);
if (pos == INF)
dp[i] = getDis(i, n + 1);
else {
ll Dis = getDis(i, pos);
dp[i] = dp[pos] + Dis + (mod - Dis % mod);
}
insert(root, 0, mod - 1, sum[i] % mod, i);
}
int q; cin >> q;
while (q--) {
int t0; cin >> t0;
ll pos = queryPos2(t0);
if (pos == INF)
cout << t0 + sum[n + 1] << '
';
else {
ll tmp = getDis(0, pos);
cout << t0 + tmp + (mod - (t0 + tmp) % mod) + dp[pos] << '
';
}
}
return 0;
}
以上是关于校内训练2019-11-08新婚快乐的主要内容,如果未能解决你的问题,请参考以下文章