牛牛的猜球游戏

PS:如果读过题了可以跳过题目描述直接到题解部分

暂无提交链接

题目

题目描述

牛牛和牛妹在玩猜球游戏,牛牛首先准备了 10 个小球,小球的编号从 0~9。首先,牛牛把这 10 个球按照从左到右编号为 0,1,2,3...9 的顺序摆在了桌子上,接下来牛牛把这 10 个球用 10 个不透明的杯子倒扣住。

牛牛接下来会按照一定的操作顺序以极快的速度交换这些杯子。

换完以后他问牛妹你看清楚从左到右的杯子中小球的编号了么?

由于牛妹的动态视力不是很好,所以她跑来向你求助。你在调查后发现牛牛置换杯子其实是有一定原则的。

具体来讲,牛牛有一个长度大小为 n 的操作序列。

操作序列的每一行表示一次操作都有两个非负整数 a,b,表示本次操作将会交换从左往右数第 a 个杯子和从左往右数第 b 个杯子(a 和 b 均从 0 开始数)。请注意是换杯子,而不是直接交换 a 号球和 b 号球。

牛牛和牛妹一共玩了 m 次猜球游戏,在每一轮游戏开始时,他都将杯子中的小

球重置到从左往右依次为 0,1,2,3...9 的状态。

然后在第 i 轮游戏中牛牛会按照操作序列中的第 l[i] 个操作开始做,一直做到第 r[i] 个操作结束(l 和 r 的编号从 1 开始计算)。

由于你提前搞到了牛牛的操作序列以及每一次游戏的 l,r。请你帮助牛妹回答出牛牛每一轮游戏结束时,从左至右的杯子中小球的编号各是多少。

输入格式

首先输入一行两个正整数 n,m,表示操作序列的长度以及进行游戏的次数。

接下来 n 行每行两个非负整数 a,b,表示交换左数第 a 个杯子和左数第 b 个杯子。(a,b 均从 0 开始数起)

接下来 m 行每行两个正整数 l,r 表示该轮游戏中牛牛从第 l 个操作开始做,一直做到第 r 个操作结束。(l 和 r 的编号从 1 开始计算)

输出格式

对于每一轮游戏,输出一行 10 个非负整数,表示从左至右每一个杯子中小球,输出的整数之间用空格隔开,行末不允许有多余空格。

样例

输入

5 3

0 1

1 2

2 3

0 1

9 0

1 5

5 5

3 5

输出

9 1 3 0 4 5 6 7 8 2

9 1 2 3 4 5 6 7 8 0

9 0 3 2 4 5 6 7 8 1

 数据范围

对于 30% 的测试数据,保证 1≤n,m ≤500

对于 60% 的测试数据,保证 1≤n, m≤4×10^4

对于 60% 以外另 10% 的数据,保证输入的 a,b∈ {0,1,2}

对于 100% 的测试数据,保证 1≤n,m≤10^5, 0≤a,b≤ 9 , 1≤l≤r≤n

题解

写这道题的题解主要是想纪念一下我的第一道被卡常卡掉的题。

但毕竟还是一道题解,所以我还是把我的思路大概讲一下。

这道题最暴力的方法当然是暴力,但肯定会T掉,所以我们需要进行优化。

其他题解很多都是用的回滚莫队,但我没有(说实话是我不会

我的想法是既然一个数可以交换,那么它无论这次操作之前或是之后的操作对它当前操作的相对位移都是没有影响的。

所以我用数组 p[i][j] 表示第 i 次操作相对没操作之前的数 j 的相对位移,取向右为正。

这样在取 l 到 r 次操作中间的时候只需要把第 r 次操作与第 l-1 次操作相减就好了。

具体的相减办法,我们可以找一下规律:

以样例为例

Ap0

1

23456789
00000000000
11-100000000
B22-1-10000000
33-1-1-1000000
430-2-1000000
C5307-100000-9

我们以 l=3,r=5 的样例为例。

我们通过观察会发现 A+B 是操作后的数值,而 A+B+(C-B)=A+C 则是其所在的位置。

代码实现

//牛牛的猜球游戏
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
int a[100010],b[100010];
int l,r;
int w[15];
int p[100010][15];

void kd(int &x){
	int nt;
	x=0;
	while(!isdigit(nt=getchar()));
	x=nt^'0';
	while(isdigit(nt=getchar())){
		x=(x<<1)+(x<<3)+(nt^'0');
	}
}

int main(){
	register int i,j;
	kd(n);
	kd(m);
	for(i=0;i<=9;++i){
		w[i]=i;
		p[0][i]=0;
	}
	for(i=1;i<=n;++i){
		kd(a[i]);
		kd(b[i]);
		swap(w[a[i]],w[b[i]]);
		for(j=0;j<=9;++j){
			p[i][w[j]]=j-w[j];
		}
	}
	for(i=1;i<=m;++i){
		kd(l);
		kd(r);
		for(j=0;j<=9;++j){
			w[j+p[r][j]]=j+p[l-1][j];
		}
		for(j=0;j<=9;++j){
			printf("%d ",w[j]);
		}
		printf("\n");
	}
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月半流苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值