NOIP2017列队

2023-10-18 00:20
文章标签 noip2017 列队

本文主要是介绍NOIP2017列队,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Sylvia 是一个热爱学习的女♂孩子。
前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。
Sylvia 所在的方阵中有名学生,方阵的行数为 n,列数为 m。
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 编上了号码(参见后面的样例)。即:初始时,第 i行第 j列 的学生的编号是。
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 q q件这样的离队事件。每一次离队事件可以用数对(x,y) (1 \le x \le n, 1 \le y \le m)描述,表示第 x行第 y列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:
向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 x 行第 m列。
向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 n行第 m 列。
教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行 第 m 列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。
输入
输入共 q+1行。
第 1 行包含 3 个用空格分隔的正整数 n, m, q,表示方阵大小是 n 行 m 列,一共发 生了 q 次事件。
接下来 q行按照事件发生顺序描述了 q 件事件。每一行是两个整数 x, y用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 x行第 y列。
输出
按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。
在这里插入图片描述

这个题首先是动态开点线段树的模板题。
动态开点线段树用来维护有几个人被删掉,快速求出当前这行(列)的第k个人是谁。
然后就很板了啊;
AC Code(写了70行算很菜的了):

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define maxn 300005
#define maxm 300005*20
#define LL long long
using namespace std;int n,m,q,len;int tot,lc[maxm],rc[maxm],cut[maxm];
int rt[maxn];
vector<LL>item[maxn];void get(int &res){char ch;while(!isdigit(ch=getchar()));for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}int New()
{++tot;cut[tot]=lc[tot]=rc[tot]=0;return tot;
}void Insert(int &now,int l,int r,int pos)
{if(!now)now=New();cut[now]++;if(l==r) return;int mid=(l+r)>>1;if(pos<=mid) Insert(lc[now],l,mid,pos);else Insert(rc[now],mid+1,r,pos);
}int Query(int now,int l,int r,int pos)
{if(l==r) return l;int mid=(l+r)>>1,siz=mid-l+1-cut[lc[now]];if(siz>=pos) return Query(lc[now],l,mid,pos);else return Query(rc[now],mid+1,r,pos-siz);
}LL Delrow(int u,LL suc)
{int tmp;Insert(rt[n+1],1,len,tmp=Query(rt[n+1],1,len,u));LL ret=(tmp<=n ? 1ll*tmp*m:item[n+1][tmp-n-1]);item[n+1].push_back(suc==-1?ret:suc);return ret;
}LL Delcol(int u,int v)
{int tmp;Insert(rt[u],1,len,tmp=Query(rt[u],1,len,v));LL ret=(tmp<m ? 1ll*(u-1)*m+tmp:item[u][tmp-m]);item[u].push_back(Delrow(u,ret));return ret;
}int main(){//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);int u,v;get(n),get(m),get(q);len=max(n+q,m+q);	for(int i=1;i<=q;i++){get(u),get(v);printf("%lld\n",(v==m)?Delrow(u,-1):Delcol(u,v));}
}

但是出题人扬言要卡线段树,放树状数组过。
但树状数组做这个题就有点曲折了。
第一个难点,线段树的二分结构使得我们可以边二分边求前缀和,做到复杂度O(logn),但是树状数组在蒟蒻的眼中一般来说都只能二分套树状数组O(log^2n),这复杂度首先就不对了。
树状数组也是可以O(logn)并且常数更小的完成这个任务的。
如果你学习过zkw线段树,你可以发现树状数组就是一个省了一半空间的线段树加上中序遍历。
实际上还是可以实现的,具体看代码。
第二个难点,树状数组不能动态开点。
但是你会发现,其实我们需要维护的信息,因为我们是惰性删除,我们只需要求实际的位置就可以,而这针对于各行各列都是相对独立的,那么完全没有必要一遍求出答案,每一行分别处理,最后利用最后一列求出答案就行。

AC Code(50行尚可):

#define maxn 300005
#define LL long long
using namespace std;char cb[1<<15],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &res){ char ch;for(;!isdigit(ch=getc()););for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0'); }int n,m,q;
int siz,tr[maxn*4],x[maxn],y[maxn],ry[maxn];
vector<LL>vec[maxn],qry[maxn];
inline void upd(int now,int val){ for(;now<=siz;now+=now&-now) tr[now]+=val; }
inline int qsum(int now){ int ret = 0;for(;now;now-=now&-now) ret+=tr[now];return ret; }
inline int Find(int x)
{int ret=1,sum=0;for(;;ret<<=1) if((ret<<1)-tr[ret<<1]>=x) break;if(ret-tr[ret] == x) return ret;sum = tr[ret];for(int k=ret>>1;k;k>>=1) if((ret+k)-tr[ret+k]-sum<x) ret+=k,sum+=tr[ret];return ret+1;
}int main()
{read(n),read(m),read(q),siz=max(n,m)+q;for(int i=1;i<=q;i++){read(x[i]),read(y[i]);if(y[i]!=m) qry[x[i]].push_back(i);}for(int i=1,sz;i<=n;i++){sz=qry[i].size();for(int j=0;j<sz;j++)upd(ry[qry[i][j]]=Find(y[qry[i][j]]),1);for(int j=0;j<sz;j++)upd(ry[qry[i][j]],-1);}for(int i=1,row;i<=q;i++){LL ans = 0;row=Find(x[i]),upd(row,1);ans=(row <=n ? 1ll * row * m : vec[n+1][row-n-1]);if(y[i]!=m)vec[x[i]].push_back(ans),ans=(ry[i] <= m-1 ? 1ll * (x[i]-1)*m+ry[i] : vec[x[i]][ry[i]-m]);vec[n+1].push_back(ans);printf("%lld\n",ans);}
}

这篇关于NOIP2017列队的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/228910

相关文章

[NOIP2017 提高组] 列队

个人难度:Medium+/Hard- 题目描述 给你一个矩阵,每次操作删除一个数 a x , y a_{x,y} ax,y​,然后第 x x x 行 y y y 右边所有数左移一位填补空位,然后第 m m m 列 x x x 上边所有数下移一位填补空位,最后把 a x , y a_{x,y} ax,y​ 放到 n n n 行 m m m 列填补空位。每次操作时求 a x ,

循环列队的循序结构

</pre><pre name="code" class="cpp">//1.队列顺序结构的定义#define MAXQSIZE 100typedef struct {QElemType base[MAXQSIZE];//静态数组int front;//队列头指针int rear;//队列尾指针}SqQueue;//解决队列的假溢出方法//1.将循序列队臆造为一个环状空间。尾指针指向头指针

【NOIP2017提高A组模拟9.5】NYG的背包

Description Input Output Sample Input 输入1: 3 5 3 1 4 8 8 3 输入2: 3 7 9269 21366 1233 7178 23155 16679 23729 15062 28427 939 6782 24224 9306 22778 13606 5 22367 17444 5442 164

【NOIP2017模拟9.3A组】摘果子

Description Input Output Sample Input 7 9 39 6 13 2 22 6 7 4 -19 5 28 6 -17 1 2 1 3 2 4 1 5 4 6 2 7 3 Sample Output 52 Solution 就是树上背包问题,有一个很经典的做法 按照dfs序反着来dp,那么f[i][j]表示的就

【NOIP2017提高A组模拟8.10】文本编辑器

Description Input 第一行是初始内容 之后按照题目要求 Output 对于每个命令,按照要求输出 Sample Input goodykc 11 I R u I R l L L L L R D R < R D R S Sample Output T T T T T T T F T T goodluck Sol

Java代码实现列队基本操作

Java实现列队基本操作 队列的定义: 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。 (1)允许删除的一端称为队头(Front)。 (2)允许插入的一端称为队尾(Rear)。 (3)当队列中没有元素时称为空队列。 (4)队列亦称作先进先出(First In First Out)的线性表,简称为FIFO表。    队列的修改是依先进先出

洛谷 P3956 [NOIP2017 普及组] 棋盘

思路:优先队列 其实本来想用双端队列进行解答的,但是呢,题目中有一个比较特殊的地方,那就是可以施展魔法让没有颜色的格子变成有颜色的格子,这样的话你如果普通的按照双端队列那样存储,会得不偿失,因为你将面临两个问题:何时才能涂颜色?涂颜色应该涂什么颜色最好呢?所以pass。 这里看了题解才知道要用优先队列进行优化。先从最折磨人的施展魔法这里讲起吧...... 这个魔法问题,我们其实可以转化连续跳

数据结构->手把手教入门栈与列队(基础)

✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉 🍎个人主页:橘橙黄又青-CSDN博客 1.什么是栈 1.1栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除 操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out) 的原则。 压栈:栈的插入操作叫做进栈/

NOIP2017 - 宝藏

LibreOJ链接 Description 给出一个\(n(n\leq12)\)个点\(m(m\leq1000)\)条边的带权无向图,求该图的一棵生成树,使得其边权×该边距根的深度之和最小。 Solution 既然\(n\leq12\),可以猜测是状压DP。 定义\(f[dpt][s][s_1]\)表示一棵深度为\(dpt\),点集为\(s\),最深的(深度为\(dpt\))的点的集合为\(s_

【NOIP2017模拟】猫种花

题面-猫   信息组最近猫成灾了!隔壁物理组也拿猫没办法.信息组组长只好去请神刀手来帮他们消灭猫.信息组现在共有n 只猫(n 为正整数),编号为1 到n,站成了一个环,第i 只猫的左边是第i-1 只猫,右边是第i+1 只猫.特别的,第1 只猫的左边是第n 只猫,第n 只猫的右边是第1 只猫.每只猫拥有价值,表示消灭它能给信息组组长带来的声誉.注意,有的猫价值为负数,这意味着消灭它会损害组长的声