【loj2319】[NOIP2017]列队 Splay(卡过)

题目描述

给出一个 $n\times m$ 的矩阵,第 $i$ 行第 $j$ 列的数为 $(i-1)\times m+j$ 。

现在有 $q$ 次操作,每次操作给出位置 $(x,y)$ ,取出 $(x,y)$ 位置的数,然后令 $(x,y+1)\sim (x,m)$ 的所有数向左(列减小)平移一格,再令 $(x+1,m)\sim (n,m)$ 的所有数向上(行减小)平移一格,最后将取出的数放到位置 $(n,m)$ 。

求每次取出的数是多少。


题解

Splay

考场上觉得正解一定不是Splay(以为带log的数据结构都不考),就只写了80分暴力。

事实上正解还真是数据结构 = =

本题使用Splay来做就变成了真真正正的模拟题。

我们对 每行除最后一列以外的部分 和 最后一列 建Splay,那么就只需要实现:取出区间中第 $k$ 个数、将一个数插入到序列最后一个数的后面。直接使用Splay模拟即可。

然而初始的数有 $n\times m$ 个,不能直接加入到Splay中。注意到原来的数都是等差数列,因此可以参考 bzoj3678 的方法,打上等差数列标记即可。

时间复杂度 $O(n\log n)$ ,常数较大,在loj上可过,在luogu上需要开O2才能过。

#include <cstdio>
#include <cctype>
#include <algorithm>
#define N 10000010
using namespace std;
typedef long long ll;
int fa[N] , c[2][N] , s[N] , si[N] , root[300010] , tot;
ll w[N] , d[N];
inline char nc()
{
	static char buf[100000] , *p1 , *p2;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
}
inline int read()
{
	int ret = 0; char ch = nc();
	while(!isdigit(ch)) ch = nc();
	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
	return ret;
}
inline void pushup(int x)
{
	si[x] = si[c[0][x]] + si[c[1][x]] + s[x];
}
inline void rotate(int &k , int x)
{
	int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
	if(y == k) k = x;
	else c[c[1][z] == y][z] = x;
	fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
	pushup(y) , pushup(x);
}
inline void splay(int &k , int x)
{
	int y , z;
	while(x != k)
	{
		y = fa[x] , z = fa[y];
		if(y != k)
		{
			if((c[0][y] == x) ^ (c[0][z] == y)) rotate(k , x);
			else rotate(k , y);
		}
		rotate(k , x);
	}
}
int find(int k , int x)
{
	if(x <= si[c[0][k]]) return find(c[0][k] , x);
	if(x > si[c[0][k]] + s[k]) return find(c[1][k] , x - si[c[0][k]] - s[k]);
	x -= si[c[0][k]];
	if(x > 1) fa[++tot] = k , w[tot] = w[k] , d[tot] = d[k] , s[tot] = x - 1 , c[0][tot] = c[0][k] , pushup(tot) , fa[c[0][tot]] = c[0][k] = tot;
	if(x < s[k]) fa[++tot] = k , w[tot] = w[k] + d[k] * x , d[tot] = d[k] , s[tot] = s[k] - x , c[1][tot] = c[1][k] , pushup(tot) , fa[c[1][tot]] = c[1][k] = tot;
	w[k] += d[k] * (x - 1) , s[k] = 1;
	return k;
}
inline int split(int &rt , int l , int r)
{
	int a = find(rt , l) , b = find(rt , r + 2);
	splay(rt , a) , splay(c[1][rt] , b);
	return c[0][c[1][rt]];
}
int main()
{
	int n = read() , m = read() , i , q = read() , x , y , u , v;
	for(i = 1 ; i <= n ; i ++ )
	{
		root[i] = ++tot , w[tot] = 1ll * (i - 1) * m + 1 , d[tot] = 1 , s[tot] = m - 1 , si[tot] = m + 1;
		c[0][root[i]] = ++tot , s[tot] = 1 , si[tot] = 1 , fa[tot] = root[i];
		c[1][root[i]] = ++tot , s[tot] = 1 , si[tot] = 1 , fa[tot] = root[i];
	}
	root[n + 1] = ++tot , w[tot] = m , d[tot] = m , s[tot] = n , si[tot] = n + 2;
	c[0][root[n + 1]] = ++tot , s[tot] = 1 , si[tot] = 1 , fa[tot] = root[n + 1];
	c[1][root[n + 1]] = ++tot , s[tot] = 1 , si[tot] = 1 , fa[tot] = root[n + 1];
	while(q -- )
	{
		x = read() , y = read();
		if(y == m)
		{
			printf("%lld\n" , w[u = split(root[n + 1] , x , x)]);
			c[0][fa[u]] = 0 , pushup(fa[u]) , pushup(fa[fa[u]]) , fa[u] = 0;
			split(root[n + 1] , n , n - 1);
			fa[u] = c[1][root[n + 1]] , c[0][fa[u]] = u , pushup(fa[u]) , pushup(fa[fa[u]]);
		}
		else
		{
			printf("%lld\n" , w[u = split(root[x] , y , y)]);
			c[0][fa[u]] = 0 , pushup(fa[u]) , pushup(fa[fa[u]]) , fa[u] = 0;
			v = split(root[n + 1] , x , x);
			c[0][fa[v]] = 0 , pushup(fa[v]) , pushup(fa[fa[v]]) , fa[v] = 0;
			split(root[x] , m - 1 , m - 2);
			fa[v] = c[1][root[x]] , c[0][fa[v]] = v , pushup(fa[v]) , pushup(fa[fa[v]]);
			split(root[n + 1] , n , n - 1);
			fa[u] = c[1][root[n + 1]] , c[0][fa[u]] = u , pushup(fa[u]) , pushup(fa[fa[u]]);
		}
	}
	return 0;
}

 

转载于:https://www.cnblogs.com/GXZlegend/p/8323622.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值