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;
}

【CSP-SJX 2019】T4 散步的更多相关文章

  1. 上午小测3 T1 括号序列 && luogu P5658 [CSP/S 2019 D1T2] 括号树 题解

    前 言: 一直很想写这道括号树..毕竟是在去年折磨了我4个小时的题.... 上午小测3 T1 括号序列 前言: 原来这题是个dp啊...这几天出了好几道dp,我都没看出来,我竟然折磨菜. 考试的时候先 ...

  2. @CSP模拟2019.10.16 - T3@ 垃圾分类

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 为了保护环境,p6pou建设了一个垃圾分类器. 垃圾分类器是一个 ...

  3. CSP/NOIP 2019 游记

    Day0 打牌 Day1 \(T1\) 没开\(ull\), 不知道有几分 \(T2\) \(N^2\)暴力+链, 没搞出树上做法, \(70\)分 \(T3\) 标准\(10\)分( 感觉今年省一稳 ...

  4. 【置顶】CSP/S 2019退役祭

    标题没错,今年就是我的最后一年了. 才高一啊,真不甘心啊. DAY1(之前的看前几篇博客吧) T1 现在没挂 T2 貌似是树形DP,跑到80000的深度时挂了,于是特判了链的情况,大样例过了,现在没挂 ...

  5. CSP-S2019 游记

    想到正解,不一定赢 全部打满,才是成功 Day 0 首先很感谢各位朋友送的贺卡!!! 早上10点的高铁.今年可以直接在汕头站坐高铁不用专门跑到潮汕站了,1h->15min车程,巨大好评. 虽然离 ...

  6. 【Luogu】P6232 [eJOI2019]挂架 题解

    这道题跟CSP/S 2019 D1T1有点像. 我们先来模拟一下 \(n=4\) 的情况, 不难得出,最后的衣架挂钩顺序: 下标: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 ...

  7. 第十八次CSP认证游记 | 2019.12.15

    CSP认证的考试是Haogod介绍的,取得一定成绩之后能有机会参加CCSP的分赛区和全国决赛.这次来参加认证要感谢老师的奔走为我们申请学校的报销,虽然最终因为这不是比赛所以报名费和差旅费下不来,但是老 ...

  8. COCI 2018/2019 CONTEST #2 T4 Maja T5Sunčanje Solution

    COCI 2018/2019 CONTEST #2 T4 T5 Solution abstract 花式暴力 #2 T5 Sunčanje 题意 按顺序给你1e5个长方形(左下角坐标&& ...

  9. CSP 2019 游记

    Day -32 开坑. 没什么好说的,等个 5 天等初赛(应该叫第一轮认证)挂掉之后就能弃坑了. 今天开始停课,虽然每天只停半天,但是感觉还是特别的舒服~ 然而得等初赛过了才能全天停课-- 没关系,熬 ...

随机推荐

  1. springmvc的文件上传和JWT图形验证码

    相关pom依赖 <dependency> <groupId>commons-fileupload</groupId> <artifactId>commo ...

  2. go语言设计模式之factory

    factory.go package factory import ( "errors" "fmt" ) const ( Cash = 1 DebitCard ...

  3. Vue动态加载图片图片不显示

    图片是放在assets文件夹下的 使用require进行解决 图片不显示的原因 在webpack,将图片放在assets中,会将图片图片来当做模块来用,因为是动态加载的,所以url-loader将无法 ...

  4. 解决 eclipse出现 Address already in use: bind,以及tomcat端口占用

    在项目开发中,有时候我们发现回报这个错:解决 eclipse出现 Address already in use: bind 产生的原因是端口占用,比如你的8080端口,已经有一个进程在访问使用,但是你 ...

  5. 新终端必须source /etc/profile的解决办法,同时解决变色问题

    Linux环境变量文件 /etc/profile:在登录时,操作系 统定制用户环境时使用的第一个文件 ,此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. /etc /enviro ...

  6. docker搭建zookeeper集群

    1.在官网拉取镜像 docker pull zookeeper 2.根据镜像启动zookeeper容器 docker run -itd --name zookeeper1 -h zookeeper1 ...

  7. CometOJ10C 鱼跃龙门

    题目链接 problem 实际上就是对于给定的\(n\)求一个最小的\(x\)满足\(\frac{x(x+1)}{2}=kn(k\in N^*)\). solution 对上面的式子稍微变形可得\(x ...

  8. 自己收集的好玩的JS特效(持续更新)

    放到我自己的服务器上了. 网 scale.html 樱花 sakura.html

  9. golang数据结构之栈

    stack.go package stack import ( "errors" "fmt" ) type Stack struct { MaxTop int ...

  10. Java连载43-访问静态方法其他注意事项、static关键字

    一.其他注意点 1.对于带有static的方法,我们讲过需要用“类名.”的方式进行访问,但是其实我们使用“引用.”的方式也是可以进行访问这个方法的举例. package com.bjpowernode ...