Codeforces Round 919 (Div. 2) D - Array Repetition 模拟、二分

文章介绍了两种方法解决涉及大量数组操作的问题,包括插入和复制,以及对特定位置数字的高效查询。一种是使用模拟的方法,另一种是利用二分搜索进行优化。这两种方法都考虑了数组大小和查询效率的复杂性。
摘要由CSDN通过智能技术生成

题目链接 

题目大意

        有一空数组a,进行n次操作。有两种操作类型,1:在数组后插入一个数x ( 1≤x≤n ),2:将数组复制x (1≤x≤10^9)份后接入数组后面。有q次询问,每次询问输出第k位数字是什么。

1<=n,q<=10^5,  1≤k≤min(10^18,c), 其中 c 是完成所有 n 操作后的数组大小

解法1:模拟

        思路

                将操作看成是一轮一轮进行的,每一轮是先进行多次操作1之后再进行一次操作2,即先加数再复制。每一轮至少复制一次,最多有10^18个数字,故最多有\log_{2}10^{_{}^{18}}(约==60)。

num[i],记录进行完第i轮之后能得到多少个数字。

rep[i],记录第i轮得到的序列有多少个相同的序列(循环了几次),即复制的x次j加上本身的1次

vector<int>v[N],v[i],表示第i轮新增加的数。

                每次询问,从后往前找,在第几轮第一次达到了 k 位数,假设在第 i 轮达到了 k 位数,则先计算该轮中每次循环几个数,即 p=num[i] / rep[i],再求余 t=x%p(t 表示处在循环里的第几个数),当 t==0(即最后一个数)时,为方便查找让 t=p 。再判断 t 是否小于等于num [ i-1 ],若为真则表示要找的这个数不是这一轮加入的,让 x=t,继续循环,直到找到这个数是在某一轮加入的,然后输出该数;若为假,则表示这个数在上一轮中还不存在,是这一轮加入的,则从 v[i] 中找到这个数(v [i] [ t- num [ i ]- 1]),输出。

        code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 1e18;
int num[100];//记录进行完第i轮之后能得到多少个数字

int rep[100]; 
//记录第i轮得到的序列有多少个相同的序列(循环了几次)
//即复制的x次再加上本身的1次

vector<int>v[100];//v[i],表示第i轮新增加的数


signed query(int x, int i) {
	while (1) {
		while (num[i - 1] >= x) i--;//找在哪一轮达到了第x位数

		int k = num[i] / rep[i];//一个循环里面有几个数

		int t = x % k;//处在第几个

		if (t == 0) t = k;//==0,表示处在最后一个数,即第k个数,为方便输出让t=k

		if (t <= num[i - 1]) x = t;//不是在这一轮加入的,继续往前找

		else return v[i][t - num[i - 1] - 1];//是在这一轮加入的,则直接输出
	}
}
void solve() {
	int n, q;
	cin >> n >> q;

	//初始化,最多不超过60轮
	for (int i = 1; i <= 65; ++i) {
		v[i].clear();
		num[i] = 0;
		rep[i] = 1;
		//rep初始值一定要为1,当 n 次操作不以完整的一轮结尾时,即以操作1结尾,
		// 当rep=0时,在算这一轮有几个数 p= num[i] / rep[i] 时会出错,
		// 所有初始值要为1,表示本身就是一次循环
	}
	int b, x;
	int now = 1;
	while (n--) {
		cin >> b >> x;
		if (num[now - 1] >= INF) continue;
		//查询的k最大到10^18,当超过这个范围就不管了
		//复制的份数x,(1<=x<=10^9),当num[i-1]足够大时会超

		if (b == 1) {
			v[now].push_back(x);//v[i]表示第i轮新增的数
			num[now] = num[now - 1] + v[now].size();
			//为防止第n次操作不以操作2结尾时,新加入的这些数直接形成新的一轮
		}
		else {
			int add = x;//表示实际需要复制的次数

			if (num[now - 1]) {
				add = min(x, INF / num[now - 1]);
			}
			//当复制后得到的数超过所需的10^18个时,取后者,将a的大小限制在10^18的范围内

			num[now] = (num[now - 1] + v[now].size()) * (add + 1);
			//当前轮的个数=(上一轮的个数+新增)乘以复制的次数加1

			rep[now] = add + 1;//表示有几个相同的序列,即复制数加上本身

			now++;
		}
	}
	while (q--) {
		int k = 1;
		cin >> k;
		cout << query(k, now) << " ";
	}
	cout << '\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--) solve();
	return 0;
}

解法2:二分

        思路

                看每一次操作,num[i] 表示第 i 次操作后能得到多少个数字。last[i] 表示第 i 次操作后的最后一个数是谁。

                对于每次询问,在 num 中找第一个大于他的数。分情况讨论,若当前轮是操作 2 得到的,且 x % num[ i - 1] != 0: 答案就是前 i -1次操作中的第 x % num[ i -1]大的数字,若 x % num[ i - 1] == 0 则答案是上一轮的最后一个数,即 last[ i -1];若当前轮是操作 1 得到的,因为进行的是操作1,故 num[ i ]=num[[ i -1]+1, 又因为找的是num 中找第一个大于他的数,故找到的是num[ i ],而答案即为 i-1 轮的最后一个数 last[ i-1] ,并且此时 x%num[ i -1]==0 (x正好等于num[i-1])。

        code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10, INF = 1e18;
int num[N], last[N];
int n, q;

signed query(int x) {
	while (1) {
		int pos = upper_bound(num + 1, num + n + 1, x) - num;
		//求出第一次数组中的数字数量大于x需要的操作数量

		int k = x % num[pos - 1];
		if (!k) return last[pos - 1];
		//如果第pos次操作类型是1: 答案就是做完pos-1次操作的结果
		//如果第pos次操作类型是2并且x % num[pos - 1] == 0: 
		// 答案就是做完pos-1次操作的最后一个数字

		x %= num[pos - 1];
		//如果第pos次操作类型是2并且x % num[pos - 1] != 0:
		//  答案就是前pos-1次操作中,第x % num[pos-1]大的数字
	}
}
void solve() {
	cin >> n >> q;
	int op, x;
	for (int i = 1; i <= n; ++i) {
		cin >> op >> x;
		if (op == 1) {
			num[i] = num[i - 1] + 1;
			last[i] = x;
		}
		else {
			int add = x;//表示实际需要复制的次数
			if (num[i - 1]) {
				add = min(x, INF / num[i - 1]);
				//当复制后得到的数超过所需的10^18个时,取后者,将a的大小限制在10^18的范围内
			}
			num[i] = num[i - 1] * (add + 1);
			last[i] = last[i - 1];
		}
	}
	int k;
	while (q--) {
		cin >> k;
		cout << query(k) << " ";
	}
	cout << '\n';
 }
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--) solve();
	return 0;
}

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【CodeforcesCodeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值