NOIP2017 队列

前置技能:动态开点线段树

题意:

Sylvia 是一个热爱学习的女♂孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。

Sylvia 所在的方阵中有 n \times mn×m 名学生,方阵的行数为 nn ,列数为 mm 。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n \times mn×m 编上了号码(参见后面的样例)。即:初始时,第 ii 行第 jj 列 的学生的编号是 (i-1)\times m + j(i1)×m+j 。

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 qq 件这样的离队事件。每一次离队事件可以用数对 (x,y) (1 \le x \le n, 1 \le y \le m)(x,y)(1xn,1ym) 描述,表示第 xx 行第 yy 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

  1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 xx 行第 mm 列。

  2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 nn 行第 mm 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 nn 行 第 mm 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

 

 

q<=500 50分 模拟

只有500组询问,妥妥的暴力

但是如果开出n*m的数组,空间就会炸

不难发现每个人的出队只会影响当前行和最后一

所以只要维护这p行和最后一列的信息,空间复杂度:O(p*m+n)

procedure dfs1;
begin
  for i:=1 to q do readln(x[i],y[i]);
  for i:=1 to q do
  begin
    if c[x[i]]=0 then c[x[i]]:=i;
    for j:=1 to m do
        a[i,j]:=(x[i]-1)*m+j;
  end;
  for i:=1 to n do
    b[i]:=(i-1)*m+m;
  for i:=1 to q do
  begin
    ii:=c[x[i]];
    if y[i]=m then k:=b[x[i]]
    else k:=a[ii,y[i]];
    writeln(k);
    for j:=y[i] to m-2 do
      a[ii,j]:=a[ii,j+1];
    a[ii,m-1]:=b[x[i]];
    for j:=x[i] to n-1 do
      b[j]:=b[j+1];
    b[n]:=k;
  end;
end;

x=1

全部操作只涉及第一行和最后一列

可以开两个数组维护第一行和最后一列

整个过程可以总结为两个操作

1.从序列中找出第k个数并删除

2.把删除的数叫入到另一个序列

我们可以开两棵线段树维护这些信息

procedure build(k,l,r:longint);
var mid:longint;
begin
  sum[k]:=r-l+1;
  if l=r then exit;
  mid:=(l+r) div 2;
  build(k*2,l,mid);
  build(K*2+1,mid+1,r);
end;
function query(k,l,r,pos:longint):int64;
var mid:longint;
begin
  if l=r then exit(l);
  mid:=(l+r) div 2;
  if (pos<=sum[k*2]) then exit(query(k*2,l,mid,pos))
    else exit(query(k*2+1,mid+1,r,pos-sum[k*2]));
end;
procedure change(k,l,r,pos:longint);
var mid:longint;
begin
  if (l=r) then
  begin
    sum[k]:=0;
    exit;
  end;
  mid:=(l+r) div 2;
  if (pos<=mid) then change(k*2,l,mid,pos)
    else change(k*2+1,mid+1,r,pos);
  sum[k]:=sum[k*2]+sum[k*2+1];
end;
procedure build2(k,l,r:longint);
var mid:longint;
begin
  if (l=r) then
  begin
    if (l<=n) then sum2[k]:=1;
    exit;
  end;
  mid:=(l+r) div 2;
  build2(k*2,l,mid);
  build2(k*2+1,mid+1,r);
  sum2[k]:=sum2[k*2]+sum2[k*2+1];
end;
function query2(k,l,r,pos:longint):int64;
var mid:longint;
begin
  if (l=r) then exit(l);
  mid:=(l+r) div 2;
  if (pos<=sum2[k*2]) then exit(query2(k*2,l,mid,pos))
    else exit(query2(k*2+1,mid+1,r,pos-sum2[k*2]));
end;
procedure change2(k,l,r,pos,w:longint);
var mid:longint;
begin
  if (l=r) then
  begin
    sum2[k]:=w;
    exit;
  end;
  mid:=(l+r) div 2;
  if (pos<=mid) then change2(k*2,l,mid,pos,w)
    else change2(k*2+1,mid+1,r,pos,w);
  sum2[k]:=sum2[k*2]+sum2[k*2+1];
end;
procedure dfs2;
var a:array[0..300005]of int64;
    ans,x,y:int64;
begin
  build(1,1,m-1);
  i:=1;j:=m;
  a[1]:=j;
  build2(1,1,n+q);
  for i:=1 to q do
  begin
    readln(x,y);
    if (y<=sum[1]) then
    begin
      ans:=query(1,1,m-1,y);
      writeln(ans);
      change(1,1,m-1,ans);
      change2(1,1,n+q,n+i,1);
      a[n+i]:=ans;
    end
    else
    begin
      y:=y-sum[1];
      y:=query2(1,1,n+q,y);
      writeln(a[y]);
      change2(1,1,n+q,y,0);
      change2(1,1,n+q,n+i,1);
      a[n+i]:=a[y];
    end;
  end;
end;

100分

100分和80分其实很相似了,思想上没有太大的变化

想到很多位置都没有动,所以用动态开点线段树

另一个不同点是位置数组需要动态维护,所以开个vector存即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=300005,M=6000100;
vector<ll>S[N];
int n,m,Q,tot,root[N],ls[M],rs[M],v[M],cnt=0;

inline void Modify(int &rt,int l,int r,int sa){
    if(!rt)rt=++cnt;
    v[rt]++;
    if(l==r)return ;
    int mid=(l+r)>>1;
    if(sa<=mid)Modify(ls[rt],l,mid,sa);
    else Modify(rs[rt],mid+1,r,sa);
}

inline int qry(int rt,int l,int r,int k){
    if(l==r)return l;
    int mid=(l+r)>>1;
    int sum=mid-l+1-v[ls[rt]];
    if(k<=sum)return qry(ls[rt],l,mid,k);
    return qry(rs[rt],mid+1,r,k-sum);
}

inline ll caline(int x){
    int r=qry(root[n+1],1,tot,x);
    Modify(root[n+1],1,tot,r);
    return r<=n?1ll*(r-1)*m+m:S[n+1][r-n-1];
}
inline ll calrow(int x,int y){
    int r=qry(root[x],1,tot,y);
    Modify(root[x],1,tot,r);
    return r<m?1ll*(x-1)*m+r:S[x][r-m];
}

void work()
{
    int x,y;
    ll ret,tmp;
    scanf("%d%d%d",&n,&m,&Q);
    tot=Max(n,m)+Q;
    while(Q--){
        scanf("%d%d",&x,&y);
        if(y==m){
            ret=caline(x);
            S[n+1].push_back(ret);
            printf("%lld\n",ret);
        }
        else{
            ret=calrow(x,y);
            printf("%lld\n",ret);
            S[n+1].push_back(ret);
            tmp=caline(x);
            S[x].push_back(tmp);
        }
    }
}

int main()
{
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    work();
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/kousak/p/9175031.html