[CSP-SJX2019]散步(模拟)
题目大意
公园内有 \(n\) 个人正在散步,随着天色渐晚,所有人准备回家离开公园。公园的结构是一个首尾相连的环形图,它共有 \(m\) 个出口,为了方便叙述,我们将人从 \(1\sim n\) 编号,将出口按逆时针顺序从 \(1\sim m\) 编号。
公园总长 \(L\) 米,我们令 \(1\) 号出口所在的位置为 \(0\) 米,则 编号为 \(i(2\le i\le m)\) 的出口在 \(1\) 号出口逆时针方向 \(a_i\) 米的位置上,其中 \(a_i\) 严格递增 ,即 \(i(1\le i \le m)\) 号出口与 \(i+1\) 号出口相邻,由于公园是环形图,故 \(m\) 号出口与 \(1\) 号出口也相邻。每个出口还有一个通行限制 \(l_i\),表示最多有 \(l_i\) 个人能从 \(i\) 号出口离开。
所有人回家时将按自己的朝向,可能是顺时针方向,也可能是逆时针方向不断前行,当他们走到一个还能离开的出口时,将从该出口离开公园。特别地,当两个人同时走到一个只能允许 \(1\) 个人离开的出口时,编号小的那个人能从该出口离开,编号较大的人将继续前进。
现在给定 \(n\) 个人所在的起始位置与他们的前进方向,请你求出每个人从哪个出口离开,若编号为 \(i\) 的 人从 \(k_i\) 号出口离开,你只需要给出 \(i\times k_i\) 的异或和,即:
\[(1\times k_1) \operatorname{xor} (2\times k_2) \operatorname{xor}\cdots \operatorname{xor} (n\times k_n) \]
其中 \(\operatorname{xor}\) 是位异或运算。特别地若一个人最后无法离开,则他的 \(k_i = 0\)。
数据范围
对于 \(12\%\) 的数据: \(n, m, L \le 10\)
对于 \(32\%\) 的数据: \(n, m \le 100 , L \le 1000\)
对于 \(52\%\) 的数据: \(n, m \le 1000\)
另有 \(20\%\) 的数据: \(n, m \le 10000\) ,所有 \(s_i = 0\)
对于 \(100\%\) 的数据: \(1 \le n,m \le 2 \times 10^5 , 2 \le L \le 10^9 , 1\le a_i \le L 1\le l_i \le n, s_i\in\{0,1\} , 0\le b_i \le L\)
解题思路
思路挺显然,代码却不是这样。
暴力就是每个点找到最近的那个出口,然后用大根堆维护一下即可,时间复杂度 \(\Theta(n^2\log n)\),当然我们发现这个过程中的瓶颈在于更新 queue,如果每次暴力扫一遍就可以做到 \(\Theta(n^2)\),这提醒我们每次找到最大的而不是更新以后立刻放到队列里,又发现更新的人将要去同一个出口,所以我们可以用线段树维护,这样要修改的人就在一个连续段上,从一个出口到下一个出口,这些人的目标距离都要增加这些距离,那么区间加即可,然后查询时询问最值即可。
这么说起来好像很简单,但是实现起来并不是这样的,你需要写两颗线段树来维护顺时针和逆时针,需要处理到同一个出口的连续段的左右端点,需要处理换上跨过 1,m 的情况。还要用链表删除出口,这样的话就可以 A 掉了。自己做出这题感觉还是蛮有成就的呐。目前是洛谷最优解。
/*
/> フ
| _ _|
/`ミ _x 彡
/ |
/ ヽ ?
/ ̄| | | |
| ( ̄ヽ__ヽ_)_)
\二つ
*/
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '\n') {
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y, 0); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y, 0); }
const int N = 200050;
const int inf = 1e9;
pair<int, int> Inf(inf, inf);
struct chain {
int pre, nxt;
int dp, dn;
}ch[N];
int jue(int x) { return x < 0 ? -x : x; }
int pos[N], lim[N], m, L, n;
struct SegTree {
#define ls p << 1
#define rs ls | 1
#define Pa pair<int, int>
Pa mn[N<<2];
int add[N<<2], L[N], R[N], f[N], rp[N], rrp[N], n;
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
struct node {
int t, num, to;
bool operator < (const node &i) const {
return to < i.to;
}
}val[N];
void build(int p, int l, int r) {
if (l == r) return rp[val[l].num] = l, mn[p] = MP(val[l].t, val[l].num), void();
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
mn[p] = min(mn[ls], mn[rs]);
}
void spread(int p) {
add[ls] += add[p], add[rs] += add[p];
mn[ls].fi += add[p], mn[rs].fi += add[p];
add[p] = 0;
}
void change(int p, int l, int r, int L, int R, int d) {
if (L <= l && r <= R) return mn[p].fi += d, add[p] += d, void();
int mid = (l + r) >> 1; if (add[p]) spread(p);
if (L <= mid) change(ls, l, mid, L, R, d);
if (R > mid) change(rs, mid + 1, r, L, R, d);
mn[p] = min(mn[ls], mn[rs]);
}
void Change(int p, int l, int r, int x) {
if (l == r) return mn[p] = Inf, void();
int mid = (l + r) >> 1; if (add[p]) spread(p);
if (x <= mid) Change(ls, l, mid, x);
else Change(rs, mid + 1, r, x);
mn[p] = min(mn[ls], mn[rs]);
}
Pa query(void) { return n ? mn[1] : Inf; }
void init(int nu, int t) { val[++n] = node{0, nu, t}; }
void prework(int fl = 0) {
sort(val + 1, val + n + 1); int tp = 1;
for (int i = 1;i <= n; i++) {
while (pos[tp] < val[i].to) tp++;
int t = val[i].to; rpos[val[i].num] = i;
if (pos[tp] == val[i].to) val[i].to = tp;
else val[i].to = tp - fl;
val[i].t = jue(t - pos[val[i].to]);
if (val[i].to == m + 1) val[i].to = 1;
if (val[i].to == val[i-1].to) R[find(i-1)] = i, f[i] = i - 1;
else L[i] = R[i] = f[i] = i, rrp[val[i].to] = i;
}
if (n) build(1, 1, n);
}
int rpos[N];
int bl(int x) { return val[find(x)].to; }
void delit(int t, int nt, int dis, int ty) {
if (!rrp[t] || !n) return ;
int nw = rrp[t]; rrp[t] = 0;
if (L[nw] > R[nw]) {
change(1, 1, n, 1, R[nw], dis);
change(1, 1, n, L[nw], n, dis);
}
else change(1, 1, n, L[nw], R[nw], dis);
if (rrp[nt]) {
if (ty) L[nw] = L[rrp[nt]], f[rrp[nt]] = nw;
else R[nw] = R[rrp[nt]], f[rrp[nt]] = nw;
}
rrp[nt] = nw, val[nw].to = nt;
}
inline int to(int x) { return val[find(rpos[x])].to; }
}A, B;
ll ans;
void delit(int t) {
if (ch[t].nxt == t) { write(ans); exit(0); }
A.delit(t, ch[t].nxt, ch[t].dn, 0), B.delit(t, ch[t].pre, ch[t].dp, 1);
int nt = ch[t].nxt, pr = ch[t].pre, dis = ch[t].dp + ch[t].dn;
ch[nt].pre = pr, ch[pr].nxt = nt, ch[nt].dp = ch[pr].dn = dis;
}
int main() {
// freopen ("hs.in","r",stdin);
// freopen ("hs.out","w",stdout);
read(n), read(m), read(L);
for (int i = 2;i <= m; i++) read(pos[i]);
for (int i = 1;i <= m; i++) read(lim[i]);
pos[m + 1] = L, pos[0] = L - pos[m];
for (int i = 1;i <= m; i++) {
ch[i].pre = i - 1;
ch[i].nxt = i + 1;
ch[i].dp = jue(pos[i] - pos[ch[i].pre]);
ch[i].dn = jue(pos[i] - pos[ch[i].nxt]);
if (!ch[i].pre) ch[i].pre = m;
if (ch[i].nxt > m) ch[i].nxt = 1;
}
pos[m + 1] = L;
for (int i = 1, s, b;i <= n; i++)
read(b), read(s), b ? B.init(i, s) : A.init(i, s);
A.prework(), B.prework(1);
for (int i = 1;i <= n; i++) {
Pa a = A.query(), b = B.query();
if (a < b) {
int t = A.to(a.se); ans ^= (ll)a.se * t; lim[t]--;
// write(a.se, ' '), write(t);
A.Change(1, 1, A.n, A.rp[a.se]);
if (!lim[t]) delit(t);
}
else {
int t = B.to(b.se); ans ^= (ll)b.se * t; lim[t]--;
// write(b.se, ' '), write(t);
B.Change(1, 1, B.n, B.rp[b.se]);
if (!lim[t]) delit(t);
}
}
write(ans);
return 0;
}
/*
3 2 5
2
2 1
0 1
1 3
0 4
10 3 20
15 19
5 3 2
0 2
1 5
0 4
0 3
0 16
1 18
1 12
0 11
0 9
0 1
10 5 50
5 9 19 40
1 4 2 1 1
0 16
1 45
0 12
0 8
1 16
0 7
0 43
1 1
0 14
0 33
*/