NOIP 2017 列队(线段树二分+动态开点)

7 篇文章 0 订阅

这道题在洛谷可以找到:https://www.luogu.org/problemnew/show/P3960#sub

题目描述

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

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

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

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

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

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

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

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

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

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

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

输入输出格式

输入格式:

 

输入共 q+1q+1q+1 行。

第 1 行包含 3 个用空格分隔的正整数 n,m,qn, m, qn,m,q,表示方阵大小是 nnn 行 mmm 列,一共发 生了 qqq 次事件。

接下来 qqq 行按照事件发生顺序描述了 qqq 件事件。每一行是两个整数 x,yx, yx,y,用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 xxx 行第 yyy 列。

 

输出格式:

 

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。

 

输入输出样例

输入样例#1: 复制

2 2 3 
1 1 
2 2 
1 2 

输出样例#1: 复制

1
1
4

说明

【输入输出样例 1 说明】

列队的过程如上图所示,每一行描述了一个事件。 在第一个事件中,编号为1 11 的同学离队,这时空位在第一行第一列。接着所有同学 向左标齐,这时编号为 22 2的同学向左移动一步,空位移动到第一行第二列。然后所有同 学向上标齐,这时编号为4 4 4的同学向上一步,这时空位移动到第二行第二列。最后编号 为1 11 的同学返回填补到空位中。

【数据规模与约定】

数据保证每一个事件满足 1≤x≤n,1≤y≤m1 \le x \le n,1 \le y \le m1≤x≤n,1≤y≤m

 

这个题应该有不止一种做法,这里还是用线段树吧(貌似只会这个...orz)

这里开n+1棵线段树,前n棵维护n行,第n+1棵维护第m列,

分两种情况,当y==m时,找到第n+1棵线段树,将第x个删除,加到第m列最后

当y!=m时,

找到第x棵线段树,将y位置的值删除并插入第n+1棵线段树最后,找到第m列(即第n+1棵线段树)中x的位置,将其删除插入到第x棵线段树最后。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define scn(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int maxn=6e5+10,maxm=2e7+10;
int n,m,q,Max,rt[maxn],pos,tot=0;
vector<ll>v[maxn];
struct tree{
 int sum;
 int ls,re;
}t[maxm];
void kd(int& u,int l,int r)
{
    if(!u)
        u=++tot;
    t[u].sum++;
    if(l<r){
        int mid=(l+r)>>1;
        if(mid>=pos)
            kd(t[u].ls,l,mid);
        else
            kd(t[u].re,mid+1,r);
    }
}
int query(int u,int l,int r,int k)
{
    if(l==r)
        return l;
    int mid=(l+r)>>1;
    int num=mid+1-l-t[t[u].ls].sum;
    if(num>=k)
        return query(t[u].ls,l,mid,k);
    else return query(t[u].re,mid+1,r,k-num);
}
ll workl(int x,ll w)
{
      pos=query(rt[n+1],1,Max,x);
     kd(rt[n+1],1,Max);
     ll ans=pos<=n? 1ll*m*pos:v[n+1][pos-n-1];
     v[n+1].push_back(w?w:ans);
     return ans;
}
ll workr(int x,int y)
{
     pos=query(rt[x],1,Max,y);
    kd(rt[x],1,Max);
    ll ans=pos<m? 1ll*(x-1)*m+pos:v[x][pos-m];
    v[x].push_back(workl(x,ans));
    return ans;
}
int main()
{
     while(~scanf("%d%d%d",&n,&m,&q)){
         tot=0;
        Max=q+max(n,m);
        int x,y;
        while(q--){
            scanf("%d%d",&x,&y);
            if(y==m)
               printf("%lld\n",workl(x,0));
            else
               printf("%lld\n",workr(x,y));
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值