NOIP2017_D2T3 列队
题目略(各大OJ上都有)
题解:
我们可以观察到一个规律,就是每次移动都只会改变一行和最后一列(最后一列是关键),于是我们就可以把这个方阵划分成这样
每个划分的区间只需要快速支持这两个操作:
- 从中查询一个位置的值并删除
- 向末尾添加一个值
因为最多只有q个添加操作,q有而一个序列原本就有这么长,用高级数据结构显然太浪费,动态开点的线段树就可以搞定,两个操作一次时间复杂度都是,新开的空间也是,而一次查询2个或4个操作就可以搞定。
总时间复杂度:,空间同
那么这个动态开点线段树怎么写呢:
把区间总范围设为,每个节点都设一个值cal表示节点区间包含的有效节点个数,叶节点再加一个val表示值,在这里,删除是指把此位置标为无效节点,并没有从序列中抹除,而2操作则是直接把最后一个有效点后的位置更新成要添加的值。
删除时直接在线段树上做二分,把叶节点cal改成0,上面的路径更新一下即可。
设一个Xi数组表示第i个序列的Xi位置还没有被插入过,每次插入时就可以精准定位,查完后Xi++,虽然不能保证和当前最后一个有效点紧挨着,但一定不会超出范围,这样效果是一样的。
下图展示了样例的第一个操作的过程:
有一步最为关键:如果一开始一个一个地更新初始方阵的每一个值,空间时间都要炸飞。可是不更新的话,前n或m-1个位置的值都不同,后q个位置又都是无效位置,没法操作。这时我们可以这样想,一个节点没有被建立,说明这个区间范围内没有被更新过,而我们知道节点所在序列,具体区间范围,又知道初始状态,那么我们就可以直接计算出有关这个节点的所有数据,而如果节点已被建立,直接使用节点数据即可。
还有就是此题卡常严重,非树状数组的童鞋们最好还是小心一点~~
代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N=3e5+5;
struct Node{
Node* l;
Node* r;
int cal;
long long val;
Node(){
cal=val=0;
l=r=NULL;
}
};
int n,m,q,Len,Xi[N];
Node* Root[N];
Node* NewNode(){
return new Node();
}
inline int Cal(Node* o,int l,int r,int Limit)
{
if(o!=NULL)
return o->cal;
r=min(Limit,r);
return (l>r?0:r-l+1);
}
void update(Node* o,int l,int r,int k,long long val0,int Limit)
{
o->cal++;
if(l==r){
o->val=val0;
return;
}
int mid=(l+r)>>1,l_cal=Cal(o->l,l,mid,Limit);
if(k<=mid)
{
if(o->l==NULL){
o->l=NewNode();
o->l->cal=l_cal;
}
update(o->l,l,mid,k,val0,Limit);
}
else
{
if(o->r==NULL){
o->r=NewNode();
o->r->cal=o->cal-1-l_cal;
}
update(o->r,mid+1,r,k,val0,Limit);
}
}
long long query(Node* o,int l,int r,int k,int Limit,int id)
{
o->cal--;
if(l==r){
if(!o->val)
{
long long id0=id,m0=m,l0=l;
o->val=(id0?((id0-1ll)*m0+l0):(l0*m0));
}
return o->val;
}
int mid=(l+r)>>1,l_cal=Cal(o->l,l,mid,Limit);
if(k<=l_cal)
{
if(o->l==NULL){
o->l=NewNode();
o->l->cal=l_cal;
}
return query(o->l,l,mid,k,Limit,id);
}
else
{
if(o->r==NULL){
o->r=NewNode();
o->r->cal=o->cal+1-l_cal;
}
return query(o->r,mid+1,r,k-l_cal,Limit,id);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<=n;i++)
{
Root[i]=NewNode();
Root[i]->cal=m-1;
Xi[i]=m;
}
Root[0]->cal=n;
Xi[0]=n+1;
Len=max(n,m)+q;
while(q--)
{
int x,y;
long long ans;
scanf("%d%d",&x,&y);
ans=query(Root[0],1,Len,x,n,0);
if(y!=m)
{
long long ans1=query(Root[x],1,Len,y,m-1,x);
update(Root[x],1,Len,Xi[x],ans,m-1);
Xi[x]++;
ans=ans1;
}
printf("%lld\n",ans);
update(Root[0],1,Len,Xi[0],ans,n);
Xi[0]++;
}
return 0;
}