「JOISC 2020 Day3」收获
题目大意
IOI 农场是一个种植苹果的农场,以位于一个巨大的环形湖周边而闻名。
在 IOI 农场,共有 \(N\) 个员工,从 \(1\) 到 \(N\) 标号。共有 \(M\) 棵苹果树,从 \(1\) 到 \(M\) 标号。湖的周长为 \(L\) 米。
在初始时刻,第 \(i\) (\(1 \leq i \leq N\)) 位员工站在离湖最北端顺时针 \(A_i \,\mathrm m\) 米的位置,第 \(j\) (\(1 \leq j \leq M\)) 棵苹果树在离湖最北端顺时针 \(B_i \,\mathrm m\) 的位置。保证这 \(N + M\) 个整数 \(A_i, B_i\) 互不相同。
由于 IOI 农场苹果树是经过改良的特殊品种,一棵树同时只能结一个苹果。同时,如果一棵树上的苹果被摘掉了,在恰好 \(C \,\mathrm s\) 后会长出一个苹果。
在初始时刻,每棵树上都有一个苹果,同时每个员工开始沿着顺时针方向移动。员工的移动速度是 \(1 \,\mathrm{m/s}\)。如果一个员工在某一时刻到达了一颗长有苹果的苹果树,他会摘掉这个苹果 (如果在到达时恰好长出苹果,员工也会摘掉)。这里我们忽略员工摘苹果的时间。
K 主席是 IOI 农场的股东。因为你是 IOI 农场的一名管理人员,K 主席会不断问你每个员工的工作效率。更一般的,K 主席会有 \(Q\) 个问题,第 \(k\) (\(1 \leq k \leq Q\)) 个问题的形式如下:
询问前 \(T_k \,\mathrm s\) 中,第 \(V_k\) 个员工一共收获了多少个苹果 (注意包含第 \(T_k \,\mathrm s\) 末收获的苹果)。
请编写一个程序来回答这些询问。
题解
很震撼的题。
首先默认把 \(A, B\) 排序。
问题转化
观察到这个 \(C\) 是一个定值,也就是苹果的生长周期是一个定值。我们考虑一下这个定值带给了我们什么想法。
因为人的移动速度是相同的, \(C\) 为定值,那么一个人摘下这个苹果以后,下一个摘这个苹果的人是固定的。不妨我们令某个人 \(i\) 摘完以后下一个摘这个苹果的人的编号是 \(p_i\) 。那么我们连边 $i \rightarrow p_i $ 。
那么我们这样就可以得到一棵关于人的基环内向树。
考虑 \(p_i\) 如何计算。在 \(A\) 中查找在 \(\mod L\) 意义下 \(A_i - C\) 前的第一个数的下标就是 \(p_i\), 用一个 \(\text{set}\) 维护即可。
然后我们考虑确定这棵树的边权, 不妨令边权 \(w_i\) 表示两个人之间摘到苹果的时间差, 那么 \(w_i\) 是满足如下条件的最小正整数。
- \(w_i \geq C\)
- \(w_i \equiv A_i - A_{p_i} \pmod L\)
可以直接计算得出 \(w_i = \lfloor\frac{C + L - ((A_i - A_{p_i} + L) \mod L) - 1}{L}\rfloor \times L + A_i - A_{p_i}\) 。
可以发现, 一个苹果的路径只会在一棵基环树上面跑, 我们现在对每棵基环树分开考虑。
对于一个人, 我们对于其是否在基环树的环上分开讨论。
对于不在环上的人的处理
不妨对每棵苹果树处理出一个数对 \((v_0, t_0)\) 表示 \(t_0\) 时刻 \(v_0\) 号人第一次摘下了这个苹果。那么现在我们的问题就是求某个点 \(v\) 的子树里有多少数对 \((v_0, t_0)\) 满足 \(t_0 + \text{dist}(v_0, v) \leq t\) , 也就是 \(t_0 + dep_{v_0} \leq t + dep_v\) 。加上 \(\text{dfs}\) 序的限制,可以在离散化以后二维数点求出。
对于在环上的人的处理
同样考虑处理一个数对 \((v_0, t_0)\) 表示 \(t_0\) 时刻 \(v_0\) 号人第一次在环上摘下了这个苹果。我们可以倍增处理这个东西。然后我们开始讨论这棵基环树的环。不妨设这个环的形态是 \(c_0 \leftarrow c_1 \leftarrow c_2 \leftarrow \cdots \leftarrow c_{s - 1} \leftarrow c_s(c_0)\) 。记 \(dep_i\) 表示环上边长的前缀和, \(P\) 表示环长。
设这个数对为 \((c_i, t0)\) , 人的位置为 \(c_j\), 不妨设 \(i \geq j\) 。那么这个苹果对这个人的贡献如下
考虑对贡献右边进行变化
这样一棵苹果树对应一个参数 \(\delta_t = t_0 + dep_i\) , 一个人对应一个参数 \(\delta_y = t + dep_j\) 。由于有对 \(P\) 做除法的操作, 不妨按照套路,设 \(\delta_t = P \times q_t + r_t, \delta_y = P \times q_y + r_y\) 。带进原式得
那么可以先对于 \(q_y \geq q_t\) 的点, 求出\(q_y - q_t\)之和, 然后每次求有多少点满足 \(r_y \geq r_t\) 。同样是一个二维数点。
然后再考虑 \(i < j\) 的情况,发现把式子化简以后就是所有答案为正数的情况下答案减少了\(1\)。 那么我们把 \(i < j\) 的情况和前面同等考虑, 然后减去满足 \(i < j, t_0 + dep_i \leq t + dep_j\) 的点对个数即可。
所有询问全部离线树状数组处理, 时间复杂度 \(O(N \log N)\) 。
#include <bits/stdc++.h>
using namespace std;
#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
#define Lep(i, l, r) for(int i = (l); i < (r); i ++)
#define Rep(i, l, r) for(int i = (l - 1); i >= (r); i --)
#define pb push_back
#define fi first
#define se second
using i64 = long long;
using uint = unsigned int;
using ui64 = unsigned long long;
using pii = pair<int, int>;
using vi = vector<int>;
namespace io {
struct io {
io() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
}
} unname;
struct read {
operator int () { int x; cin >> x; return x; }
operator i64 () { i64 x; cin >> x; return x; }
operator char () { char x; cin >> x; return x; }
operator double () { double x; cin >> x; return x; }
operator string () { string x; cin >> x; return x; }
} rd;
} ;
using io :: rd;
const int N = 4e5 + 10;
namespace Unique {
vector<i64> unique(vector<i64> &vec) {
sort(vec.begin(), vec.end());
auto end = unique(vec.begin(), vec.end());
vector<i64> res;
for (auto it = vec.begin(); it != end; ++ it) res.push_back(* it);
return res;
}
} ;
#define lowbit(x) (x & -x)
struct Bit {
i64 c[N];
vector<int> bck;
void upd(int x, i64 y) {
bck.push_back(x);
for (; x < N; x += lowbit(x)) c[x] += y;
}
i64 qry(int x) {
i64 ans = 0;
for (; x; x -= lowbit(x)) ans += c[x];
return ans;
}
i64 qry(int l, int r) { return qry(r) - qry(l - 1); }
void clr(int x) {
for (; x < N; x += lowbit(x)) c[x] = 0;
}
void clr() {
for (auto x : bck) clr(x); bck.clear(); //n = 0;
}
} bt;
#undef lowbit
int n, m, L, C;
inline int reduce(int x, int mod) { return (x % mod + mod) % mod; }
int A[N], B[N];
int p[N];
vector<pair<int, int> > e[N];
int bel[N], tree_number, root[N], sz[N], number, dfn[N], oncir[N], fa[N][20], lg[N];
i64 dep[N], ew[N];
void dfs(int x, int fx) {
dfn[x] = ++ number; sz[x] = 1; bel[x] = tree_number; fa[x][0] = fx;
Lep (i, 1, 20) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(auto p : e[x]) {
int y = p.first, w = p.second;
if(y == root[tree_number]) oncir[y] = 1, oncir[x] = 1;
else dep[y] = dep[x] + w, dfs(y, x), oncir[x] |= oncir[y], sz[x] += sz[y];
}
}
i64 Ans[N];
struct Three { i64 a, b, c; } ;
struct Qry {
int v; i64 t; int id;
} ;
vector<Qry> qrys1, qrys2;
struct Point { i64 x, y; } ;
struct Line { i64 l1, r1, h, id, type; } ;
struct Mat { i64 l1, r1, l2, r2; } ;
/* Two-side count nodes */
namespace Count {
vector<i64> twoside_count(vector<Mat> &mt, vector<Point> &pt) {
vector<Line> lns;
vector<i64> ans(mt.size());
lep (i, 0, (int) mt.size() - 1) {
lns.push_back( {mt[i].l1, mt[i].r1, mt[i].l2 - 1, i, -1} );
lns.push_back( {mt[i].l1, mt[i].r1, mt[i].r2, i, 1} );
}
sort(lns.begin(), lns.end(), [] (Line a, Line b) { return a.h == b.h ? a.type < b.type : a.h < b.h; } );
sort(pt.begin(), pt.end(), [] (Point a, Point b) { return a.y < b.y; } );
int j = 0;
bt.clr();
for (auto p : lns) {
while (j < pt.size() && pt[j].y <= p.h) {
bt.upd(pt[j].x, 1); j ++;
}
ans[p.id] += p.type * bt.qry(p.l1, p.r1);
}
return ans;
}
}
signed main() {
#ifdef FILEIN
freopen("1.in", "r", stdin);
#endif
n = rd; m = rd; L = rd; C = rd;
lep (i, 1, n) A[i] = rd;
lep (i, 1, m) B[i] = rd;
/* Build Tree */
lep (i, 1, n) {
auto it = upper_bound(A + 1, A + 1 + n, reduce(A[i] - C, L)) - A - 1;
if(! it) it = n;
p[i] = it;
int w = (C + L - reduce(A[i] - A[p[i]], L) - 1) / L * L + reduce(A[i] - A[p[i]], L);
e[p[i]].push_back( {i, w} );
ew[i] = w;
}
lep (i, 2, n) lg[i] = lg[i >> 1] + 1;
lep (i, 1, n) if (! bel[i]) {
++ tree_number;
int j = i;
while(! bel[j]) { bel[j] = tree_number; j = p[j]; }
root[tree_number] = j;
dfs(j, 0);
}
int q = rd;
lep (i, 1, q) {
int v = rd; i64 t = rd;
if(! oncir[v]) qrys1.push_back( {v, t, i} );
else qrys2.push_back( {v, t, i} );
}
/* Not on circle */
vector<pair<int, i64> > pairs;
lep (i, 1, m) {
auto it = upper_bound(A + 1, A + 1 + n, B[i]) - A;
if (it == 1) it = n; else it --;
int v0 = it, t0 = reduce(B[i] - A[it], L);
pairs.push_back( {v0, t0} );
}
vector<Mat> mat;
vector<Point> pnt;
vector<i64> values, record;
for (auto p : pairs) pnt.push_back( {dfn[p.first], p.second + dep[p.first]} ), values.push_back(p.second + dep[p.first]);
for (auto p : qrys1) {
mat.push_back( {dfn[p.v], dfn[p.v] + sz[p.v] - 1, 1, dep[p.v] + p.t} ), values.push_back(dep[p.v] + p.t);
}
values.push_back(1);
values = Unique :: unique(values);
for (auto &p : pnt) p.y = lower_bound(values.begin(), values.end(), p.y) - values.begin() + 1;
for (auto &p : mat)
p.l2 = lower_bound(values.begin(), values.end(), p.l2) - values.begin() + 1,
p.r2 = lower_bound(values.begin(), values.end(), p.r2) - values.begin() + 1;
record = Count :: twoside_count(mat, pnt);
int cnt = 0;
for (auto &p : qrys1) Ans[p.id] = record[cnt], cnt ++;
/* Is on circle */
for (auto &p : pairs) {
int &v0 = p.first;
i64 &t0 = p.second;
if (! oncir[v0]) {
rep (i, 19, 0) if (fa[v0][i] && ! oncir[fa[v0][i]]) {
t0 += dep[v0] - dep[fa[v0][i]];
v0 = fa[v0][i];
}
t0 += ew[v0];
v0 = fa[v0][0];
}
}
static int vis[N], loc[N];
memset(dep, 0, sizeof(dep));
static vector<Qry> link_qry[N];
static vector<pair<i64, i64> > link_pair[N];
for (auto p : qrys2) link_qry[bel[p.v]].push_back(p);
for (auto p : pairs) link_pair[bel[p.first]].push_back(p);
lep (now, 1, tree_number) {
int rt = root[now];
int i = rt;
vector<int> cir;
while (! vis[i]) {
cir.push_back(i);
vis[i] = 1;
i = p[i];
}
cir.push_back(cir[0]);
reverse(cir.begin(), cir.end());
dep[cir[0]] = 0;
lep (i, 1, (int) cir.size() - 1) dep[cir[i]] = dep[cir[i - 1]] + ew[cir[i]];
lep (i, 0, (int) cir.size() - 2) loc[cir[i]] = i;
i64 P = dep[cir[0]];dep[cir[0]] = 0;
vector<pair<i64, i64> > tree_node;
vector<Three> people_node;
for (auto p : link_pair[now]) tree_node.push_back( {(p.second + dep[p.first]) / P, (p.second + dep[p.first]) % P} );
for (auto p : link_qry[now]) {
i64 delta = p.t + dep[p.v];
people_node.push_back( {delta / P, delta % P, p.id} );
}
sort(tree_node.begin(), tree_node.end(), [] (pair<i64, i64> a, pair<i64, i64> b) { return a.first < b.first; } );
sort(people_node.begin(), people_node.end(), [] (Three a, Three b) { return a.a < b.a; } );
values.clear();
for (auto p : tree_node) values.push_back(p.second);
for (auto p : people_node) values.push_back(p.b);
values = Unique :: unique(values);
for (auto &p : tree_node) p.second = lower_bound(values.begin(), values.end(), p.second) - values.begin() + 1;
for (auto &p : people_node) p.b = lower_bound(values.begin(), values.end(), p.b) - values.begin() + 1;
bt.clr();
i64 sum = 0; i = 0;
for (auto p : people_node) {
i64 qy = p.a, ry = p.b, id = p.c;
while (i < tree_node.size() && tree_node[i].first <= qy) {
sum += tree_node[i].first;
bt.upd(tree_node[i].second, 1);
i ++;
}
Ans[id] += 1ll * i * qy - sum + bt.qry(ry);
}
tree_node.clear();
people_node.clear();
for (auto p : link_pair[now]) tree_node.push_back( {loc[p.first], p.second + dep[p.first]} );
for (auto p : link_qry[now]) {
i64 delta = p.t + dep[p.v];
people_node.push_back( {loc[p.v], p.t + dep[p.v], p.id} );
}
values.clear();
for (auto p : tree_node) values.push_back(p.second);
for (auto p : people_node) values.push_back(p.b);
values = Unique :: unique(values);
for (auto &p : tree_node) p.second = lower_bound(values.begin(), values.end(), p.second) - values.begin() + 1;
for (auto &p : people_node) p.b = lower_bound(values.begin(), values.end(), p.b) - values.begin() + 1;
sort(tree_node.begin(), tree_node.end(), [] (pair<i64, i64> a, pair<i64, i64> b) { return a.first < b.first; } );
sort(people_node.begin(), people_node.end(), [] (Three a, Three b) { return a.a < b.a; } );
bt.clr(); i = 0;
for (auto p : people_node) {
i64 cj = p.a, vj = p.b, id = p.c;
while (i < tree_node.size() && tree_node[i].first < cj) {
bt.upd(tree_node[i].second, 1);
i ++;
}
Ans[id] -= bt.qry(vj);
}
}
lep (i, 1, q) printf("%lld\n", Ans[i]);
return 0;
}