嘛。。两年前的题目了,想起第一次参加提高组还骗了一个省二回来呢。。。跟同学吹了好久的。。。

离退役又近了一骗博客啊。。

闲聊结束。

照常化简:给定一个1-n*m编号的矩阵,每次删除一个位置,然后左边向右补,之后后面向前补,最后空出来的位置再由刚刚删去的点补上,求每次删除的点的编号。

当年也是暴力,奔着30pts就去了,结果奈何手欠数组开大了全部MLE。。。

但是正解也就从这里延伸。

首先,直接地,删除,增加,维护序列,能想到的东西差不多就是平衡树了。。。

但是,考场上大打Splay绝对不可能(日常也不可能打,我是连线段树都能写错的蒟蒻)

所以果断放弃。。

通过观察,晓得每次删除/填补只会有当前行和最后一列,还有最后一行的一个点变化。

于是,经过一年的思考和集训老师的解说,我明白了,可以用线段树写。

把用过的扔到后面,然后通过线段树的操作获得答案(下面再讲)

开n+1个线段树,维护每一行和最后一列。

为了防止数组出锅,我们要尽可能开大,所以要开(max(n,m)+q)*(n+1)个,然鹅这个数组应该达到了TB级别。。。

最终数据有900亿人(超级教官&&银河军训),而询问数量却很小,也就是说,有很多空间都没有用过,所以使用动态开点线段树,需要用的时候再找内存要空间。

然后来考虑怎么查询&&维护。

首先,线段树没有平衡树这么多神仙操作,所以要用公式或者记录某些东西来获得编号

  1. 如果没有动过,一切好说,直接乘法加法计算即可
  2. 如果是最后一列直接搞

问题就在于怎么查中间的。

记录一下每一行缺了多少人,(1-n之内跑了多少,下统称size),然后每次只需要比较查询的那个大小和size的大小就可以获得“方向”了。

  1. 如果size大,说明要求的东西不在原序列里,要另查
  2. 如果size小,说明我们可以继续在当前区间乱搞了

怎么乱搞呢?记录一下每一行缺的是哪个点,如果初始没有动的话直接公式搞定如果中间动过的话,就一系列骚操作通过记录的东西获得答案。

然后每次用答案去更新序列

然后就是代码了,有注释

#include<bits/stdc++.h>
#define ll long long
#define ill inline long long
using namespace std;
inline int read()
{
int x=,f=;char s=getchar();
while(s>''||s<''){if(s=='-')f=-;s=getchar();}
while(s<=''&&s>=''){x=x*+s-'';s=getchar();}
return x*f;
}
const ll maxn=6e6+;
int n,m,q;
int size[maxn];//第i行1-n中有多少缺失的(即动态开多少点)
ll tre[maxn];//每一行却的点的编号 (动态变化,每次只缺一个)
int ls[maxn];
int rs[maxn];
int R[maxn];//行/最后一列的长度(开完之后)
int cnt;
ill find_row(int l,int r,int x,int y,int &k)
{
if(!k)
k=++cnt;
size[k]++;
if(l==r)
{
if(tre[k]==)return (ll)(x-)*m+r;
else return tre[k];
}
int mid=l+r>>;
if(mid-l+-size[ls[k]]>=y)return find_row(l,mid,x,y,ls[k]);
else{ y-=mid-l+-size[ls[k]];return find_row(mid+,r,x,y,rs[k]);}
}
ill findline(int l,int r,int x,int y,int &k)
{
if(!k)
k=++cnt;
size[k]++;
if(l==r)
{
if(tre[k]==)return (ll)(l-)*m+y;
else return tre[k];
}
int mid=l+r>>;
if(mid-l+-size[ls[k]]>=x)return findline(l,mid,x,y,ls[k]);
else {x-=mid-l+-size[ls[k]];return findline(mid+,r,x,y,rs[k]);}
}
void add(int l,int r,ll v,int x,int &k)
{
if(!k)
k=++cnt;
if(l==r)
{
tre[k]=v;
return;
}
int mid=l+r>>;
if(x<=mid)add(l,mid,v,x,ls[k]);
else add(mid+,r,v,x,rs[k]);
}
int main()
{
//scanf("%d%d%d",&n,&m,&q);
n=read();
m=read();
q=read();
int r=max(n,m)+q;
cnt=n+;
while(q--)
{
int x,y;
x=read();
y=read();//scanf("%d%d",&x,&y);
if(y==m)
{
int k=n+;
ll ans=findline(,r,x,y,k);
printf("%lld\n",ans);
R[n+]++;add(,r,ans,R[n+]+n,k);
}
else
{
int k=n+;
ll ans=find_row(,r,x,y,x);
ll tem=findline(,r,x,m,k);
printf("%lld\n",ans);
R[x]++;add(,r,tem,R[x]+m-,x);
R[n+]++;add(,r,ans,R[n+]+n,k);
}
}
return ;
}

最新文章

  1. java单例的几种实现方法
  2. jquery 赋值文本框输入框
  3. EaseType缓动函数
  4. 【故障处理】ORA-12162: TNS:net service name is incorrectly specified
  5. Carthage&amp;&amp;cocopads 摘抄笔记
  6. ASP.NET执行cmd命令
  7. Oracle索引状态查询&amp;索引重建
  8. GDI+学问------ 绘制可变角度的色彩渐变效果
  9. SUSE Linux 下redis 的坑
  10. C语言的一些基础
  11. linux下网卡相关查看设置
  12. BZOJ3065(替罪羊树套线段树)
  13. Ext Js v6.2.0.103 Sencha Cmd 命令
  14. 关于联想笔记本ThinkPad E470 没有外音 插耳机却有声音的解决办法
  15. Java使用RabbitMQ之订阅分发(Topic)
  16. 队列Queue、栈LifoQueue、优先级队列PriorityQueue
  17. 移动端IOS 固定下方的输入框,点击输入框位置会变的修复
  18. etcd raft如何实现leadership transfer
  19. 计算概论(A)/基础编程练习2(8题)/5:点和正方形的关系
  20. Zookeeper注册中心的搭建

热门文章

  1. bugku 成绩单
  2. Redis的几个核心机制底层原理
  3. Error response from daemon: Get https://registry-1.docker.io/v2/library/nginx/manifests/1.14-alpine: Get https://auth.docker.io/token?scope=repository%3Alibrary%2Fnginx%3Apull&amp;service=registry.docker.
  4. 微信小程序前端页面书写
  5. 【前端词典】几个有益的 CSS 小知识
  6. windows显示文件后缀名
  7. PHP reset
  8. 算法---ALGO-3 Java K好数 蓝桥杯
  9. std::multimap
  10. Python编程系列---Python中装饰器的几种形式及万能装饰器