Description

传送门

Solution

算法1 32pts

枚举每个时刻,并枚举所有发生的时间,暴力进行更新。发现最多只需要枚举到第

\(L\)个时刻,因为是一个环,所以最多到第L个时刻,所有人就会回到原位。

算法2 52pts

发现在上面的过程中,并没有必要枚举每个时刻,因为很多时刻都是没有任何事件发生的。容易想到先预处理出距离每个人最近的出口,以及到出口的距离,那么每次取到出口距离最小的人,必定从那个出口出去。

设\(rest_i\)表示每个出口剩余可以通过的人数,当一个人从某个出口\(i\)出去后,则\(rest_i-1\),那么当一个出口的\(rest_i=0\)时,那么这个出口就没有了存在的意义,可以直接删掉。

不难想到通过某种数据结构来维护每个出口的前驱和后继,我这里用的是环状链表,当然机房大佬也有用并查集以及\(set\)实现的。所以先预处理出每个出口的前驱和后继,分别记为\(lst_i\)和\(nxt_i\)

考虑当一个人从出口\(i\)出去后,那么对于所有原本要从\(i\)出去的人,逆时针走的人的出口都变成了\(nxt_i\),顺时针走的人的出口都变成了\(lst_i\)。

于是得到一个做法,用堆维护每个人到离他最近的出口的距离和编号,堆里以距离为第一关键字、人的编号为第二关键字进行排序,每次取出堆顶。若当前记录的出口已经被删除,那么就将关于这个人的新的出口、距离信息插入到堆中;否则将这个人计入到答案的贡献中,并更新出口是否被删除以及前驱\(lst_i\)和后继\(nxt_i\)的相关信息。

因为每个人最坏情况下可能会被插入堆中\(n\)次,因此时间复杂度是\(O(n^2\log n)\)

算法3 100pts

考虑优化算法2,发现主要的时间都花费在更新每个人到出口的距离信息上

记出口\(i\)的位置为\(pos_i\)。可以发现,当一个出口被删除后,那么所有位置在\((pos_{lst_i},pos_{i}]\)之间且逆时针行走的人,其出口都变成了\(nxt_i\),且距离的增量都是\(pos_{nxt_i}-pos_i\);同样的,对于所有位置在\([pos_i, pos_{nxt_i})\)之间的且顺时针行走的人,其出口都变成了\(lst_i\),且距离的增量都是\(pos_i-pos_{lst_i}\)

于是想到用线段树来维护,进行区间修改,具体实现为:在算法2的基础上,以每个人的位置为关键字进行排序,原来取堆顶的操作改成查询线段树的最小值,而距离的更新则先用二分查找查找出需要更新的位置对应的线段树区间,并按照上述过程进行更新即可。

至于区分逆时针顺时针行走的人,既可以建立两棵线段树,也可以在按每个人的位置为关键字之前先按找行走方向为关键字进行排序,然后将线段树一分为二。

具体的实现细节以及技巧可以参照下面的代码:

(记得划到最底下)

Code

Version 1

#include <bits/stdc++.h>
using namespace std; inline int ty() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
} const int _ = 2e5 + 10;
int n, m, L, ct1, ct2, pos[_], lim[_], nxt[_], lst[_], num[_]; struct node {
int id, dir, pos, dis, tar;
node(int _id = 0, int _dir = 0, int _pos = 0, int _dis = 0, int _tar = 0) {
id = _id, dir = _dir, pos = _pos, dis = _dis, tar = _tar;
}
bool operator<(const node &rhs) const {
if (dir == rhs.dir) return pos < rhs.pos;
return dir < rhs.dir;
}
} sta[_]; #define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1) struct data {
int dis, tar, id;
data(int _dis = 0, int _tar = 0, int _id = 0) {
dis = _dis, tar = _tar, id = _id;
}
bool operator<(const data &rhs) const {
if (dis == rhs.dis) return id < rhs.id;
return dis < rhs.dis;
}
}; struct TAG {
int dis, tar;
TAG(int _dis = 0, int _tar = 0) { dis = _dis, tar = _tar; }
TAG& operator+=(const TAG &rhs) {
dis += rhs.dis;
if (rhs.tar) tar = rhs.tar;
return *this;
}
}; struct SegmentTree {
data val[_ << 2];
TAG tag[_ << 2];
void add(int p, TAG x) {
val[p].dis += x.dis;
if (x.tar) val[p].tar = x.tar;
tag[p] += x;
}
void down(int p) {
if (tag[p].dis || tag[p].tar) {
add(ls, tag[p]);
add(rs, tag[p]);
tag[p] = TAG(0, 0);
}
}
void up(int p) { val[p] = min(val[ls], val[rs]); }
void build(int p, int l, int r) {
if (l == r) {
val[p] = data(sta[l].dis, sta[l].tar, sta[l].id);
return;
}
build(ls, l, mid);
build(rs, mid + 1, r);
up(p);
}
data query(int p, int l, int r, int x, int y) {
if (x <= l && r <= y) return val[p];
down(p);
if (y <= mid) return query(ls, l, mid, x, y);
else if (x > mid) return query(rs, mid + 1, r, x, y);
else return min(query(ls, l, mid, x, mid), query(rs, mid + 1, r, mid + 1, y));
}
void change(int p, int l, int r, int x) {
if (l == r) {
val[p].dis = 0x3f3f3f3f;
return;
}
down(p);
if (x <= mid) change(ls, l, mid, x);
else change(rs, mid + 1, r, x);
up(p);
}
void modify(int p, int l, int r, int x, int y, TAG z) {
if (x <= l && r <= y) {
add(p, z);
return;
}
down(p);
if (y <= mid) modify(ls, l, mid, x, y, z);
else if (x > mid) modify(rs, mid + 1, r, x, y, z);
else modify(ls, l, mid, x, mid, z), modify(rs, mid + 1, r, mid + 1, y, z);
up(p);
}
} tr; #undef ls
#undef rs
#undef mid inline int getdis(int x, int y, int dir) {
if (dir == 0) {
if (pos[x] < pos[y]) return pos[y] - pos[x];
else return L - pos[x] + pos[y];
} else {
if (pos[x] > pos[y]) return pos[x] - pos[y];
else return pos[x] + L - pos[y];
}
} inline bool judge(int x, int y, int l, int r) {
return sta[x].pos >= l && sta[x].pos <= r && sta[y].pos >= l && sta[y].pos <= r;
} inline void modify(int l, int r, int dis, int tar, int dir) {
node tmp;
int L = (dir == 0) ? 1 : ct1 + 1;
int R = (dir == 0) ? ct1 + 1 : n + 1;
tmp.dir = dir, tmp.pos = l;
int st = lower_bound(sta + L, sta + R, tmp) - sta;
tmp.pos = r;
int ed = upper_bound(sta + L, sta + R, tmp) - sta - 1;
if (judge(st, ed, l, r))
tr.modify(1, 1, n, st, ed, TAG(dis, tar));
} inline void update1(int l, int r, int dis, int tar, int dir) {
if (l > r) {
modify(l + 1, L, dis, tar, dir);
modify(0, r, dis, tar, dir);
} else modify(l + 1, r, dis, tar, dir);
} inline void update2(int l, int r, int dis, int tar, int dir) {
if (l > r) {
modify(l, L, dis, tar, dir);
modify(0, r - 1, dis, tar, dir);
} else modify(l, r - 1, dis, tar, dir);
} int main() {
#ifndef ONLINE_JUDGE
freopen("walk.in", "r", stdin);
freopen("walk.out", "w", stdout);
#endif
n = ty(), m = ty(), L = ty();
pos[1] = 0, lst[1] = m, nxt[1] = 2;
for (int i = 2; i <= m; ++i) {
pos[i] = ty();
nxt[i] = i == m ? 1 : i + 1;
lst[i] = i - 1;
}
for (int i = 1; i <= m; ++i) lim[i] = ty();
for (int i = 1; i <= n; ++i) {
int s = ty(), b = ty(), d, t;
if (s == 0) {
++ct1;
t = lower_bound(pos + 1, pos + m + 1, b) - pos;
if (t > m) t = 1, d = L - b;
else d = pos[t] - b;
} else {
++ct2;
t = upper_bound(pos + 1, pos + m + 1, b) - pos;
--t;
d = b - pos[t];
}
sta[i] = node(i, s, b, d, t);
}
sort(sta + 1, sta + n + 1); for (int i = 1; i <= n; ++i) num[sta[i].id] = i;
tr.build(1, 1, n);
int out = m, pp = n;
long long ans = 0;
while (out && pp) {
data x = tr.query(1, 1, n, 1, n);
int id = x.id, tar = x.tar;
tr.change(1, 1, n, num[id]);
ans ^= (1ll * id * tar);
--pp, --lim[tar];
if (!lim[tar]) {
int l = pos[lst[tar]] % L, r = pos[nxt[tar]] % L, now = pos[tar] % L; update1(l, now, getdis(tar, nxt[tar], 0), nxt[tar], 0);
update2(now, r, getdis(tar, lst[tar], 1), lst[tar], 1); nxt[lst[tar]] = nxt[tar];
lst[nxt[tar]] = lst[tar];
--out;
}
}
printf("%lld\n", ans);
return 0;
}

(可能你觉得这段代码很长?不,看下面的版本。)

Version 2

#include<bits/stdc++.h>
using namespace std;
void ty(int &a){scanf("%d",&a);}
const int _ =2e5+10;
int n,m,L,ct1,ct2,pos[_],lim[_],nxt[_],lst[_],num[_],s,b,d,t,out,pp,i;
struct node{
int id,dir,pos,dis,tar;
bool operator<(const node&rhs)const{
if(dir==rhs.dir)return pos<rhs.pos;
return dir<rhs.dir;
}
}sta[_],tmp;
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (l+r>>1)
struct data{
int dis,tar,id;
bool operator<(const data&rhs)const{return dis==rhs.dis?id<rhs.id:dis<rhs.dis;}
};
struct TAG{
int dis,tar;
TAG&operator+=(const TAG&rhs){
dis+=rhs.dis; rhs.tar?(tar=rhs.tar):0;
return*this;
}
};
struct SegmentTree{
data val[_<<2];TAG tag[_<<2];
void add(int p,TAG x){val[p].dis+=x.dis;if(x.tar)val[p].tar=x.tar;tag[p]+=x;}
void down(int p){if(tag[p].dis||tag[p].tar) add(ls,tag[p]),add(rs,tag[p]),tag[p]={0,0};}
void up(int p){val[p]=min(val[ls],val[rs]);}
void build(int p,int l,int r){
if(l==r){val[p]={sta[l].dis,sta[l].tar,sta[l].id};return;}
build(ls,l,mid);
build(rs,mid+1,r);
up(p);
}
data query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y)return val[p];
down(p);
if(y<=mid)return query(ls,l,mid,x,y);
else if(x>mid)return query(rs,mid+1,r,x,y);
else return min(query(ls,l,mid,x,mid),query(rs,mid+1,r,mid+1,y));
}
void change(int p,int l,int r,int x){
if(l==r){val[p].dis=0x3f3f3f3f;return;}
down(p);
if(x<=mid)change(ls,l,mid,x);
else change(rs,mid+1,r,x);
up(p);
}
void modify(int p,int l,int r,int x,int y,TAG z){
if(x<=l&&r<=y){add(p,z);return;}
down(p);
if(y<=mid)modify(ls,l,mid,x,y,z);
else if(x>mid)modify(rs,mid+1,r,x,y,z);
else modify(ls,l,mid,x,mid,z),modify(rs,mid+1,r,mid+1,y,z);
up(p);
}
}tr;
int getdis(int x,int y,int dir){
if(dir==0){
if(pos[x]<pos[y])return pos[y]-pos[x];
else return L-pos[x]+pos[y];
}else{
if(pos[x]>pos[y])return pos[x]-pos[y];
else return pos[x]+L-pos[y];
}
}
bool judge(int x,int y,int l,int r){return sta[x].pos>=l&&sta[x].pos<=r&&sta[y].pos>=l&&sta[y].pos<=r;}
void modify(int l,int r,int dis,int tar,int dir){
int L=(dir==0)?1:ct1+1,R=(dir==0)?ct1+1:n+1;
tmp.dir=dir,tmp.pos=l;
int st=lower_bound(sta+L,sta+R,tmp)-sta;
tmp.pos=r;
int ed=upper_bound(sta+L,sta+R,tmp)-sta-1;
if(judge(st,ed,l,r))
tr.modify(1,1,n,st,ed,{dis,tar});
}
void update1(int l,int r,int dis,int tar,int dir){
if(l>r){
modify(l+1,L,dis,tar,dir);
modify(0,r,dis,tar,dir);
}else modify(l+1,r,dis,tar,dir);
}
void update2(int l,int r,int dis,int tar,int dir){
if(l>r){
modify(l,L,dis,tar,dir);
modify(0,r-1,dis,tar,dir);
}else modify(l,r-1,dis,tar,dir);
}
int main(){
ty(n),ty(m),ty(L);
pos[1]=0,lst[1]=m,nxt[1]=2;
for(i=2;i<=m;++i)ty(pos[i]),nxt[i]=i==m?1:i+1,lst[i]=i-1;
for(i=1;i<=m;++i)ty(lim[i]);
for(i=1;i<=n;++i){
ty(s),ty(b);
if(s==0){
++ct1,t=lower_bound(pos+1,pos+m+1,b)-pos;
if(t>m)t=1,d=L-b;
else d=pos[t]-b;
}else{
++ct2,t=upper_bound(pos+1,pos+m+1,b)-pos-1;
d=b-pos[t];
}
sta[i]={i,s,b,d,t};
}
sort(sta+1,sta+n+1);
for(i=1;i<=n;++i)num[sta[i].id]=i;
tr.build(1,1,n);
out=m,pp=n;
long long ans=0;
while(out&&pp){
data x=tr.query(1,1,n,1,n);
int id=x.id,tar=x.tar;
tr.change(1,1,n,num[id]);
ans^=(1ll*id*tar);
--pp,--lim[tar];
if(!lim[tar]){
int l=pos[lst[tar]]%L,r=pos[nxt[tar]]%L,now=pos[tar]%L;
update1(l,now,getdis(tar,nxt[tar],0),nxt[tar],0);
update2(now,r,getdis(tar,lst[tar],1),lst[tar],1);
nxt[lst[tar]]=nxt[tar];
lst[nxt[tar]]=lst[tar];
--out;
}
}
printf("%lld\n",ans);
return 0;
}
05-11 22:05