[CSP-SJX2019]散步(模拟)

[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


*/
上一篇:mybatis ON DUPLICATE KEY UPDATE语句转为oracle语句


下一篇:P2371 [国家集训队]墨墨的等式