题目传送门

  传送点I

  传送点II

题目大意

  (家喻户晓的题目应该不需要大意)

  (我之前咋把NOIP 2017打成了NOIP 2018,好绝望)

Solution 1 Splay

  每行一颗Splay,没有动过的地方直接一段一个点。

  最后一列单独一颗Splay。

  暴力模拟即可。

Soluion 2 Splay II

  我们考虑倒推。对于每个询问倒推出在第一次操作前时的位置。

  考虑每个出队操作对答案的影响。

  假设询问$(x, y)$,那么最后一列横坐标大于等于$x$的位置,横坐标都会加1.

  第$x$行,纵坐标大于等于$y$的位置,纵坐标都会加1.

  然后再把这个位置塞进$(x, y)$。

  然后暴力模拟就好了。

Code

 /**
* uoj
* Problem#334
* Accepted
* Time: 4027ms
* Memory: 25700k
*/
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean;
#define ll long long const int N = 3e5 + ; typedef class SplayNode {
public:
int val, tg, id;
SplayNode *ch[];
SplayNode *fa; SplayNode() { } void pushDown() {
for (int i = ; i < ; i++)
if (ch[i])
ch[i]->val += tg, ch[i]->tg += tg;
tg = ;
} int which(SplayNode* p) {
return p == ch[];
}
}SplayNode; SplayNode pool[N << ];
SplayNode* top = pool; SplayNode* newnode(int val, int id) {
top->val = val, top->id = id;
top->ch[] = top->ch[] = top->fa = NULL;
top->tg = ;
return top++;
} SplayNode* stps[N]; typedef class Splay {
protected:
void rotate(SplayNode* p) {
SplayNode *fa = p->fa, *ffa = fa->fa;
int df = fa->which(p);
SplayNode* ls = p->ch[df ^ ]; p->fa = ffa;
p->ch[df ^ ] = fa;
fa->fa = p;
fa->ch[df] = ls;
if (ls)
ls->fa = fa;
if (ffa)
ffa->ch[ffa->which(fa)] = p;
} void splay(SplayNode* p, SplayNode* fa) {
SplayNode** st = stps;
for (SplayNode* q = p; q; *(++st) = q, q = q->fa);
for ( ; st != stps; (*st)->pushDown(), st--);
for ( ; p->fa != fa; rotate(p))
if (p->fa->fa != fa)
rotate((p->fa->fa->which(p->fa) == p->fa->which(fa)) ? (p->fa) : (p));
if (!fa)
rt = p;
}
public:
SplayNode* rt; void insert(SplayNode*& p, SplayNode* fa, SplayNode* np) {
if (!p) {
p = np, np->fa = fa;
return;
}
if (p->tg)
p->pushDown();
insert(p->ch[np->val > p->val], p, np);
} boolean less_bound(int x) { // find the maximum number less than x and splay it
SplayNode* ret = NULL, *p = rt, *q = NULL;
while (p) {
q = p;
if (p->tg)
p->pushDown();
if (p->val < x)
ret = p, p = p->ch[];
else
p = p->ch[];
}
if (ret)
splay(ret, NULL);
else if (q)
splay(q, NULL);
return ret != NULL;
} void remove(SplayNode* p) {
splay(p, NULL);
if (!p->ch[]) {
rt = p->ch[];
if (rt)
rt->fa = NULL;
return;
} SplayNode* q = p->ch[];
while (q->ch[])
q = q->ch[];
splay(q, p);
q->ch[] = p->ch[];
if (p->ch[])
p->ch[]->fa = q;
rt = q;
} SplayNode* findMax() {
SplayNode* p = rt;
while (p && p->ch[]) {
if (p->tg)
p->pushDown();
p = p->ch[];
}
if (p)
splay(p, NULL);
return p;
} void insert(SplayNode* p) {
insert(rt, NULL, p);
splay(p, NULL);
} void getLis(SplayNode**& vs, SplayNode* p) {
if (!p)
return;
if (p->tg)
p->pushDown();
*(vs++) = p;
getLis(vs, p->ch[]);
getLis(vs, p->ch[]);
}
}Splay; int n, m, q;
Splay ls[N]; // maintain for (1, 1) - (n, m - 1)
Splay rs;
int xs[N], ys[N];
ll res[N]; inline void init() {
scanf("%d%d%d", &n, &m, &q);
for (int i = ; i <= q; i++)
scanf("%d%d", xs + i, ys + i);
} int uf[N]; int find(int x) {
return (uf[x] == x) ? (x) : (uf[x] = find(uf[x]));
} inline void solve() {
for (int i = ; i <= q; i++)
uf[i] = i; SplayNode *p;
for (int i = q; i; i--) {
int x = xs[i], y = ys[i];
while ((p = rs.findMax()) && p->val == n)
uf[find(p->id)] = i, rs.remove(p);
if (rs.rt) {
if (rs.less_bound(x)) {
p = rs.rt->ch[];
if (p)
p->val++, p->tg++;
} else
rs.rt->val++, rs.rt->tg++;
}
if (y == m) {
rs.insert(newnode(x, i));
} else {
Splay& sp = ls[x];
if (sp.rt) {
if (sp.less_bound(y)) {
p = sp.rt->ch[];
if (p)
p->val++, p->tg++;
} else
sp.rt->val++, sp.rt->tg++;
}
while ((p = sp.findMax()) && p->val == m)
rs.insert(newnode(x, p->id)), sp.remove(p);
sp.insert(newnode(y, i));
}
} SplayNode **pp, **pq;
for (int i = ; i <= n; i++) {
pp = stps;
ls[i].getLis(pp, ls[i].rt);
for (pq = stps; pq != pp; pq++)
res[(*pq)->id] = (i - ) * 1ll * m + (*pq)->val;
}
pp = stps;
rs.getLis(pp, rs.rt);
for (pq = stps; pq != pp; pq++)
res[(*pq)->id] = (*pq)->val * 1ll * m;
for (int i = ; i <= q; i++)
printf(Auto"\n", res[find(i)]);
} int main() {
init();
solve();
return ;
}

Splay

Solution 3 BIT

  考虑删掉的位置我们留下,如果我们知道所有按顺序被加进这一行的人,那么我们查找第$k$列的人相当于求第$k$大值。

  我们可以利用树状数组预处理出每一行除去最后一列,每个询问是第几个加入这一行的数(原来的数依次看做第一个,第二个,...)。

  然后开始处理询问。用类似的方法维护最后一列。

  这样可以算每次询问,最后一列被拿走的数,以及当且行被加入的数。

Code

 /**
* uoj
* Problem#334
* Accepted
* Time: 1673ms
* Memory: 32808b
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean; template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
} const int bzmax = ;
const int N = 3e5 + ; typedef class IndexedTree {
public:
int *ar;
int s; IndexedTree() { }
IndexedTree(int s):s(s) {
ar = new int[(s + )];
pfill(ar, ar + s + , );
} void add(int idx, int val) {
for ( ; idx <= s; idx += ((-idx) & idx))
ar[idx] += val;
} int kth(int k) {
int rt = , cur = ;
for (int b = ( << (bzmax - )); b; b >>= )
if ((rt | b) <= s && cur + ar[rt | b] < k)
rt += b, cur += ar[rt | b];
return rt + ;
}
}IndexedTree; #define ll long long
#define pii pair<int, int>
#define fi first
#define sc second int n, m, q, K;
int rk[N];
IndexedTree it;
pii qs[N];
vector<ll> lst;
vector<ll> vs[N];
vector<pii> ms[N]; inline void init() {
scanf("%d%d%d", &n, &m, &q);
K = max(n, m) + q;
it = IndexedTree(K);
for (int i = ; i <= q; i++) {
scanf("%d%d", &qs[i].fi, &qs[i].sc);
if (qs[i].sc < m)
ms[qs[i].fi].push_back(pii(qs[i].sc, i));
}
} inline void solve() {
for (int i = ; i <= K; i++)
it.add(i, );
for (int i = ; i <= n; i++) {
for (vector<pii> :: iterator it = ms[i].begin(); it != ms[i].end(); it++)
::it.add(rk[it->sc] = ::it.kth(it->fi), -);
for (vector<pii> :: iterator it = ms[i].begin(); it != ms[i].end(); it++)
::it.add(rk[it->sc], );
} ll res;
for (int i = , x, y, p; i <= q; i++) {
x = qs[i].fi, y = qs[i].sc;
it.add(p = it.kth(x), -);
res = ((p <= n) ? (p * 1ll * m) : (lst[p - n - ]));
if (y < m) {
vs[x].push_back(res);
res = ((rk[i] <= m - ) ? ((x - ) * 1ll * m + rk[i]) : vs[x][rk[i] - m]);
}
lst.push_back(res);
printf(Auto"\n", res);
}
} int main() {
init();
solve();
return ;
}

NOIP 2017 列队 - Splay - 树状数组的更多相关文章

  1. [NOIP2017]列队(树状数组)

    定义第i行为所有的点(i,j),0<j<m 可以发现,每一行是相对独立的,每一次操作只会影响到当前行和最后一列 考虑每一行和最后一列各开一个树状数组,但这样显然会爆空间 实际上,对于没有离 ...

  2. NOIP模拟 Work - 二分 + 树状数组 / ???

    题目分析 如果没有最后的注意事项,此题就是二分裸题.有了注意事项,会有两种思路: 在线:二分天数t,并在主席树上求1~t天中大于d(浪费的时间)的时间之和以及数量,答案即为:sum - d * cnt ...

  3. GZOI 2017配对统计 树状数组

    题目 https://www.luogu.com.cn/problem/P5677 分析 最开始读题的时候没有读的太懂,以为i是在选定区间内给的,实际上不是,这道题的意思应该是在l和r的区间内找出有多 ...

  4. 洛谷 P3960 [ NOIP 2017 ] 列队 —— 线段树

    题目:https://www.luogu.org/problemnew/show/P3960 NOIP 题,不用很复杂的数据结构...但又参考了许多: 要求支持维护删除第 k 个和在末尾插入的数据结构 ...

  5. 2018.10.08 NOIP模拟 栅栏(树状数组+rand)

    传送门 今天的送分题. 首先考虑每次给要围上栅栏的矩阵里的整体加上1,如果栅栏被撤销就整体减1,最后比较两个点的值是否相同来进行判断. 然而这样的效果并不理想,很容易卡掉. 进一步思考,我们第iii次 ...

  6. 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay

    正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...

  7. [noip科普]关于LIS和一类可以用树状数组优化的DP

    预备知识 DP(Dynamic Programming):一种以无后效性的状态转移为基础的算法,我们可以将其不严谨地先理解为递推.例如斐波那契数列的递推求法可以不严谨地认为是DP.当然DP的状态也可以 ...

  8. HDU-3436 Queue-jumpers 树状数组 | Splay tree删除,移动

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3436 树状数组做法<猛戳> Splay tree的经典题目,有删除和移动操作.首先要离散化 ...

  9. HDU 3436--Queue-jumpers (树状数组 or Splay Tree)

    树状数组这个真心想了好久,还是没想出来 %%% www.cppblog.com/Yuan/archive/2010/08/18/123871.html 树状数组求前缀和大于等于k的最大值,第一次看到这 ...

随机推荐

  1. update条件判断更新

    UPDATE cw_party tp, cw_shop tsSET tp.state = 3, ts.bonus_average = CASEWHEN ts.bonus_average > 0 ...

  2. 用memset设置无穷大无穷小

    memeset是以字节为单位进行赋值的,对字符数组可以直接用. 但对于int数组就不行了. 但设置无穷大来说有个技巧: 如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f ...

  3. 把html页面转化成图片——html2canvas

    test.html <div class="fx_zhezhao"></div> <div class="myImg"> & ...

  4. 限时免费 | 12月6日,广州保利洲际酒店,ABC Summit 2018云智峰会来了!

    随着科技的迅猛发展,人工智能技术也逐渐取得了各个突破.自20世纪70年代以来,作为计算机学科的一个分支,人工智能就被列为世界三大尖端技术之一.近年来,阿尔法狗战胜世界第一柯洁,使人工智能再度迎来新的热 ...

  5. vim创建程序文件自动添加头部注释/自动文件头注释与模板定义

    Vim 自动文件头注释与模板定义 在vim的配置文件.vimrc添加一些配置可以实现创建新文件时自动添加文件头注释,输入特定命令可以生成模板. 使用方法 插入模式输入模式输入seqlogic[Ente ...

  6. [No000018A]改善C#程序的建议11-20

    建议11:区别对待 == 和Equals CLR中将“相等性”分为两类:1.值相等性:两个变量包含的数值相等.2.引用相等性:两个变量引用的是内存中的同一个对象. 但并不是所有的类型的比较都是按照其本 ...

  7. GIS常用知识列举

    GIS知识分类 我认为GIS知识,大体可分为以下三类. G——测量学.地图学.误差理论等基础——测绘方面 I——数据库.开发——IS方面 S——GIS原理——结合前面两种知识的理念 第一类,是基础,有 ...

  8. spark-sql将Rdd转换为DataFrame进行操作的两种方法

    SparkConf sparkConf = new SparkConf() .setMaster("local").setAppName("ClzMap"); ...

  9. layui上传文件前端报404,实际文件已经上传成功

    原因:上传回调的方法接收的参数应该是json格式的,之前返回的是String,所以一直走异常的方法 第一种 在后台上加上@ResponseBody 第二种 @ResponseBody @Request ...

  10. Jmeter学习之-从数据库取出数据并且校验数据是否准确

    https://www.cnblogs.com/wuyonghuan/p/7479582.html 应用场景:调用某个接口像数据库中插入数据,需要在接口调用完成后查看数据更新或插入的数据是否正确的时候 ...