[CSP2019-JX] 散步

题目大意

有一个长度为 \(L\) 的环 \((2 \le L \le 10^9)\),
环上有 \(n\) 个人, \(m\) 个出口 \((1 \le n,m \le 2 \times 10^5)\).

规定第一个出口的位置为 \(0\), 并将 "到第一个出口的距离" 定义为 "从顺时针方向走到第一个路口所走的路程".
每个人有两个属性 \(t_i,\ x_i\), 分别表示这个人的前进方向 (\(0\) 为逆时针, \(1\) 为顺时针), 以及这个人到第一个出口的距离.
每个出口有两个属性 \(lim_i,\ a_i\), 分别表示这个出口的人数限制 (即最多能从这个出口出去多少人), 以及这个出口到第一个出口的距离.

在每个单位时间内, 所有人都会朝着他们的前进方向移动一个单位距离,
若有一个人 \(i\) 走到了一个人数没有达到上限的出口 \(j\), 他就会从这个出口出去, 则 \(k[i]=j\).
特别地, 若有两个人同时到达了一个只能出去一个人的出口, 则编号较小的那个人可以出去.

最终输出 \(i \times k[i]\) 的异或和, 即
\[ (1 \times k[1]) \oplus (2 \times k[2]) \oplus \dots \oplus (n \times k[n])\]
(注: \(\oplus\) 为异或符号)

思路

\(O(n^2)\) 52pts

首先, 最暴力的思路就是大模拟, 每次把每一个人往他的移动方向移一格, 然后从小到大扫一遍, 看哪个人可以出去.

可以发现, 我们其实只需要找到离他下一个出口最近的那个人, 记他离出口的距离为 \(mind\), 把所有人往前移动 \(mind\) 个单位距离就行了.
当然, 有可能出现这个人到了出口后, 发现出口已经被填满了, 那么我们就需要再去找他的下一个出口, 而这是一个最劣能达到 \(O(n)\) 的操作, 这样的话复杂度就有可能退化到 \(O(n^3)\).
所以, 我们需要用(双向的)并查集优化一下这个过程, 总复杂度为 \(O(\alpha n^3)\).

但还要注意的是, 不是每一次都能使得至少有一个人出去, 因为当前离出口最近的那个人他指向的出口已经被填满了, 所以复杂度应该是 \(O(玄学 \times \alpha n^3)\)

代码在下面.

代码

\(O(n^2)\) 52pts



#include<bits/stdc++.h>
using namespace std;
const int N=1e3+7;
const int inf=0x3f3f3f3f;
int n,m,L,d[N],nxt[N],le[N],ri[N],ans,a[N],x[N],minx=inf,nm,nn,lim[N];
bool t[N],gon[N];
int exi[N];
int dis(int i,int j){
  if(!j) return inf;
  int di=abs(x[i]-a[j]);
  if((a[j]>x[i]&&t[i])||(a[j]<x[i]&&!t[i])) di=L+1-di;
  return di;
}
int fl(int x){ return le[x]==x ?x :le[x]=fl(le[x]); }
int fr(int x){ return ri[x]==x ?x :ri[x]=fr(ri[x]); }
int main(){
  \\freopen("walk.in","r",stdin);
  \\freopen("walk.out","w",stdout);
  cin>>n>>m>>L;
  for(int i=2;i<=m;i++){
    scanf("%d",&a[i]);
    le[i]=ri[i]=i;
  }
  le[1]=ri[1]=1;
  for(int i=1;i<=m;i++) scanf("%d",&lim[i]);
  for(int i=1;i<=n;i++){
    scanf("%d%d",&t[i],&x[i]);
    for(int j=1;j<=m;j++)
      if(dis(i,j)<dis(i,nxt[i])) nxt[i]=j;
    minx=min(minx,dis(i,nxt[i]));
  }
  while(nm<m&&nn<n){
    int mind=minx; minx=inf;
    for(int i=1;i<=n;i++){
      if(gon[i]) continue;
      if(t[i]) x[i]-=mind;
      else x[i]+=mind;
      if(x[i]<0) x[i]+=L+1;
      else if(x[i]>L) x[i]-=L+1;
      if(x[i]==a[nxt[i]]){
    if(!lim[nxt[i]]){
      int tmp=0;
      while(!lim[nxt[i]]){
        if(t[i]){ le[tmp]=nxt[i]; nxt[i]=fl(nxt[i])-1; }
        else{ ri[tmp]=nxt[i]; nxt[i]=fr(nxt[i])+1; }
        if(!nxt[i]) nxt[i]=m;
        if(nxt[i]>m) nxt[i]=1;
        tmp=nxt[i];
      }
    }
    else{
      lim[nxt[i]]--;
      if(!lim[nxt[i]]) nm++;
      gon[i]=1; nn++;
      ans^=i*nxt[i];
      exi[i]=nxt[i];
    }
      }
      if(!gon[i]) minx=min(minx,dis(i,nxt[i]));
    }
  }
  printf("%d\n",ans);
  //for(int i=1;i<=n;i++) printf("%d ",exi[i]); putchar('\n');
  return 0;
}
02-12 12:03