zoukankan      html  css  js  c++  java
  • [CSP2019-JX] 散步 解题报告

    [CSP2019-JX] 散步

    题目大意

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

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

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

    最终输出 (i imes k[i]) 的异或和, 即

    [(1 imes k[1]) oplus (2 imes k[2]) oplus dots oplus (n imes k[n]) ]

    (注: (oplus) 为异或符号)

    思路

    (O(n^2)) 52pts

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

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

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

    代码在下面.

    (O(n log n)) 100pts

    稍微思考一下, 我们可以发现, 并不需要维护每一个人的位置. 因为一个人出去后, 其他人到达每一个出口的相对时间并没有改变, 每次我们只需要取 到下一个出口最近的人, 然后把它移到出口即可.

    距离最小的人 的操作可以在线段树上二分解决, 那我们再考虑把一个人从出口移出去后需要进行的操作.

    如果这个出口没有满, 则不需要进行操作, 下一次直接查找最小值即可.

    如果这个出口满了, 那么我们需要修改 原来要从这个出口出去的人离它下一个出口的位置,
    可以把这些人分为 在这个出口左边的 和 在这个出口右边的.
    对于左边的人, 把他们的目标转移到该出口右边第一个没有满的出口,
    对于右边的人, 把他们的目标转移到该出口左边第一个没有满的出口,

    我们考虑把所有人排序, 往逆时针方向走的排在左边, 往顺时针方向走的排在右边, 这两边再分别按他们的位置排序.
    进行上述操作后, 可以发现,
    在一个出口左边 且 要从这个出口出去的人 是一个连续的区间,
    在一个出口右边 且 要从这个出口出去的人 也是一个连续的区间,
    所以, 我们可以在线段树上区间修改这些人到下一个出口的距离.

    至于查找两边 第一个没有满的出口, 可以用并查集解决.

    分析一下复杂度: 每次都会让一个人从出口出去, 所以最多循环 (n) 次, 每次要查找最小值和区间修改, 还要在并查集中查找下一个出口,
    所以总复杂度为 (O(nlog n alpha))

    还有一点要注意的是, 当我们按上述策略排好序后, 每个人的新的编号和原来是不一样的,
    所以我们需要在线段树上建一个 num 数组, 表示 以 (k) 为根节点的子树中 距离下一个出口最近的人的最小编号, 以便在线段树上二分查找

    代码

    (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
    ",ans);
      //for(int i=1;i<=n;i++) printf("%d ",exi[i]); putchar('
    ');
      return 0;
    }
    

    (O(n log n)) 100pts

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+7;
    const int inf=0x3f3f3f3f;
    int n,m,L;
    int a[N],lim[N],t[N],x[N],id[N],ll[N],lr[N],rl[N],rr[N],le[N],ri[N],minx[4*N],tag[4*N],divd,d[N],nxt[4*N],dye[4*N],num[4*N],bel[N];
    int seq[N],pac[N][2],nn,nm;
    long long ans;
    bool rule(int i,int j){
      if(!(pac[i][0]^pac[j][0])) return pac[i][1]<pac[j][1];
      else return pac[i][0]<pac[j][0];
    }
    int dis(int i,int j,int ty){
      if((!ty&&i<j)||(ty&&i>j)) return abs(a[i]-a[j]);
      else if(!ty) return a[j]+L-a[i]+1;
      else return a[i]+L-a[j]+1;
    }
    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]); }
    void read(){
      cin>>n>>m>>L;
      for(int i=2;i<=m;i++) scanf("%d",&a[i]);
      for(int i=1;i<=m;i++){
        scanf("%d",&lim[i]);
        le[i]=ri[i]=i;
      }
      for(int i=1;i<=n;i++){
        scanf("%d%d",&pac[i][0],&pac[i][1]);
        if(!pac[i][0]) divd++;
        seq[i]=i;
      }
      sort(seq+1,seq+1+n,rule);
      for(int i=1;i<=n;i++){
        id[i]=seq[i];
        t[i]=pac[id[i]][0];
        x[i]=pac[id[i]][1];
      }
    }
    void pre(){
      int t1=divd,t2=1;
      while(t1&&x[t1]>a[m]){ d[t1]=L-x[t1]+1; bel[t1]=1; t1--; }
      ll[1]=t1+1; t1=1;
      if(ll[1]>divd) ll[1]=0;
      while(t2<=m){
        while(t1<=divd&&x[t1]<=a[t2]){ d[t1]=a[t2]-x[t1]; bel[t1]=t2; t1++; }
        lr[t2]=t1-1;
        if(t2!=1) ll[t2]=lr[t2-1]+1;
        t2++;
      }
      if(!lr[1]) lr[1]= ll[1] ?divd :0 ;
      else if(!ll[1]) ll[1]=1;
      t1=n; t2=m;
      rr[m]=n;
      while(t2){
        while(t1>divd&&x[t1]>=a[t2]){ d[t1]=x[t1]-a[t2]; bel[t1]=t2; t1--; }
        rl[t2]=t1+1;
        if(t2!=m) rr[t2]=rl[t2+1]-1;
        t2--;
      }
      for(int i=1;i<=m;i++){
        if(i!=1&&ll[i]>lr[i]) ll[i]=lr[i]=0;
        if(rl[i]>rr[i]) rl[i]=rr[i]=0;
      }
    }
    void rfs(int k){
      int tmp=minx[k<<1]-minx[k<<1|1];
      if(tmp<0){ minx[k]=minx[k<<1]; num[k]=num[k<<1]; }
      else if(tmp>0){ minx[k]=minx[k<<1|1]; num[k]=num[k<<1|1]; }
      else{ minx[k]=minx[k<<1]; num[k]=min(num[k<<1],num[k<<1|1]); }
    }
    void build(int k,int l,int r){
      if(l==r){ minx[k]=d[l]; num[k]=id[l]; nxt[k]=bel[l]; return; }
      int mid=(l+r)>>1;
      build(k<<1,l,mid);
      build(k<<1|1,mid+1,r);
      rfs(k);
    }
    void upd(int k,int w,int v){
      minx[k]+=w; tag[k]+=w;
      if(v) nxt[k]=dye[k]=v;
    }
    void psd(int k){
      upd(k<<1,tag[k],dye[k]);
      upd(k<<1|1,tag[k],dye[k]);
      tag[k]=dye[k]=0;
    }
    int query(int k,int l,int r,int x,int y,int id){
      if(l>=x&&r<=y) return id ?minx[k] :nxt[k];
      psd(k);
      int mid=(l+r)>>1,t1=inf,t2=inf;
      if(x<=mid) t1=query(k<<1,l,mid,x,y,id);
      if(y>mid) t2=query(k<<1|1,mid+1,r,x,y,id);
      return min(t1,t2);
    }
    void modify(int k,int l,int r,int x,int y,int w,int v){
      if(!x||!y) return;
      if(l>=x&&r<=y){ upd(k,w,v); return; }
      psd(k);
      int mid=(l+r)>>1;
      if(x<=mid) modify(k<<1,l,mid,x,y,w,v);
      if(y>mid) modify(k<<1|1,mid+1,r,x,y,w,v);
      rfs(k);
    }
    void recon(int k){
      if(nm==m) return;
      if(ll[k]){
        int t1=k,t2=0;
        do{
          ri[t2]=t1;
          t2=fr(t1);
          t1=t2+1;
          if(t1>m) t1=1;
        }while(!lim[t1]);
        if(!ll[t1]){ ll[t1]=ll[k]; lr[t1]=lr[k]; }
        else ll[t1]=ll[k];
        if(ll[k]<=lr[k]) modify(1,1,n,ll[k],lr[k],dis(k,t1,0),t1);
        else{
          modify(1,1,n,ll[k],divd,dis(k,t1,0),t1);
          modify(1,1,n,1,lr[k],dis(k,t1,0),t1);
        }
      }
      if(rl[k]){
        int t1=k,t2=0;
        do{
          le[t2]=t1;
          t2=fl(t1);
          t1=t2-1;
          if(!t1) t1=m;
        }while(!lim[t1]);
        if(!rl[t1]){ rl[t1]=rl[k]; rr[t1]=rr[k]; }
        else rr[t1]=rr[k];
        if(rl[k]<=rr[k]) modify(1,1,n,rl[k],rr[k],dis(k,t1,1),t1);
        else{
          modify(1,1,n,rl[k],n,dis(k,t1,1),t1);
          modify(1,1,n,divd+1,rr[k],dis(k,t1,1),t1);
        }
      }
    }
    void find(int k,int l,int r){
      if(l==r){
        ans^=(long long)id[l]*nxt[k];
        nn++; 
        modify(1,1,n,l,l,inf,nxt[k]);
        lim[nxt[k]]--;
        if(!lim[nxt[k]]){ nm++; recon(nxt[k]); }
        return;
      }
      psd(k);
      int mid=(l+r)>>1,t1=minx[k<<1]-minx[k<<1|1],t2=num[k<<1]-num[k<<1|1];
      if(t1<0||(t1==0&&t2<0)) find(k<<1,l,mid);
      else if(t1>0||(t1==0&&t2>0)) find(k<<1|1,mid+1,r);
    }
    int main(){
      //freopen("walk.in","r",stdin);
      //freopen("x.out","w",stdout);
      read();
      pre();
      build(1,1,n);
      while(nn<n&&nm<m) find(1,1,n);
      printf("%lld
    ",ans);
      return 0;
    }
    
  • 相关阅读:
    hdu 1455 N个短木棒 拼成长度相等的几根长木棒 (DFS)
    hdu 1181 以b开头m结尾的咒语 (DFS)
    hdu 1258 从n个数中找和为t的组合 (DFS)
    hdu 4707 仓鼠 记录深度 (BFS)
    LightOJ 1140 How Many Zeroes? (数位DP)
    HDU 3709 Balanced Number (数位DP)
    HDU 3652 B-number (数位DP)
    HDU 5900 QSC and Master (区间DP)
    HDU 5901 Count primes (模板题)
    CodeForces 712C Memory and De-Evolution (贪心+暴力)
  • 原文地址:https://www.cnblogs.com/BruceW/p/12039937.html
Copyright © 2011-2022 走看看