【每日一题】—— B. Deja Vu(Codeforces Round 907 (Div. 2))(暴力枚举、队列)

🌏博客主页:PH_modest的博客主页
🚩当前专栏:每日一题
💌其他专栏:
🔴 每日反刍
🟡 C++跬步积累
🟢 C语言跬步积累
🌈座右铭:广积粮,缓称王!

一.题目描述

在这里插入图片描述

题目大意:

给你一个长度为 n n n 的数组 a a a ,由正整数组成,和一个长度为 q q q 的数组 x x x ,也由正整数组成。
一共有 q q q 个修改。关于 i i i 1 ≤ i ≤ q 1 \leq i \leq q 1iq )的修改,对于每个 j j j ( 1 ≤ j ≤ n 1 \leq j \leq n 1jn ),使得 a j a_j aj 可以被 2 x i 2^{x_i} 2xi 整除,那么就把 2 x i − 1 2^{x_i-1} 2xi1 加到 a j a_j aj注意 x i x_i xi ( 1 ≤ x i ≤ 30 1 \leq x_i \leq 30 1xi30 ) 是个不超过 30 的正整数。
完成所有修改查询后,需要输出最终数组。

题目链接:

B. Deja Vu(Codeforces Round 907 (Div. 2))

二.思路分析

主要讲解代码一的思想,代码二就当做一种技巧吧,知道就好,不详细解释了。

  1. 首先需要根据题目推出背后的性质:如果一个数是 2 x 2^{x} 2x的倍数,那么它就可以写成k* 2 x − 1 2^{x-1} 2x1,然后转换成2k* 2 x − 1 2^{x-1} 2x1,加上 2 x − 1 2^{x-1} 2x1,就变成了(2k+1) 2 x − 1 2^{x-1} 2x1,那么肯定不能整除 2 x 2^{x} 2x,而变成了 2 x − 1 2^{x-1} 2x1的倍数,所以数据经过操作之后会降级:从原来整除 2 x 2^{x} 2x变成整除 2 x − 1 2^{x-1} 2x1
  2. 首先需要先将数组a里的元素预处理,将其分类,分到对应能整除的次方下,用队列存储下标,存储下标的好处就是,我们可以通过下标直接修改元素值;
  3. 然后枚举数组x,这边有一个重要的点, 2 x 2^{x} 2x能够整除 2 x 2^{x} 2x 2 x + 1 2^{x+1} 2x+1也能整除 2 x 2^{x} 2x,所以能整除大于 2 x 2^{x} 2x的数也需要+ 2 x − 1 2^{x-1} 2x1,操作完之后将其从原来的队列里出列,进行降级处理,放入 2 x − 1 2^{x-1} 2x1对应的队列里
    在这里插入图片描述
  4. 最后就是对数据进行一些简单的处理,详细内容见代码一。

三.代码展示

代码一(队列+预处理思想)

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#define int long long
using namespace std;

int a[200020];
int x[200020];
deque<int>heap[31];//表示2的0次方到30次方

void solve()
{
	int n,q;
	cin>>n>>q;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	for(int i=0;i<q;i++)
	{
		cin>>x[i];
	}
	for(int i=0;i<n;i++)//预处理数组a,将其放入对应的次方
	{
		for(int j=1;j<=30;j++)
		{
			if(a[i]%(1<<j)!=0)
			{
				heap[j-1].push_back(i);//队列一般都是存下标,通过下标直接修改原数组的值
				break;
			}
		}
	}
	for(int i=0;i<q;i++)
	{
		for(int j=30;j>=x[i];j--)//一个数可能能除以比它大的次方,操作完之后会降级
		{
			while(heap[j].size())
			{
				int tmp=heap[j].front();//保存下标
				heap[j].pop_front();
				//修改数组中的值
				a[tmp]+=(1<<(x[i]-1));
				heap[x[i]-1].push_back(tmp);
			}
		}
	}
	for(int i=0;i<n;i++)
	{
		cout<<a[i]<<" ";
	}
	for(int i=0;i<=30;i++)
	{
		heap[i].clear();
	}
	cout<<"\n";
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

代码二(暴力模拟)

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
#define int long long
using namespace std;

int mp[31]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824};
int flag[31]={0};
int s[200020];

void solve()
{
	memset(flag,0,sizeof(flag));
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++)
    {
    	cin>>s[i];
	}
	for(int i=0;i<q;i++)
	{
		int x;
		cin>>x;
		if(flag[x]==1)
		{
			continue;
		}
		flag[x]=1;
		for(int i=1;i<=n;i++)
		{
			if(s[i]%mp[x]==0)
			{
				s[i]+=mp[x-1];
			}
		}
		
	}
	for(int i=1;i<=n;i++)
	{
		cout<<s[i]<<" ";
	}
	cout<<"\n";
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

最后:

每日一题系列旨在养成刷题的习惯,所以对代码的解释并不会特别详细,但足够引导大家写出来,选的题目都不会特别难,但也不是特别简单,比较考验大家的基础和应用能力,我希望能够将这个系列一直写下去,也希望大家能够和我一起坚持每天写代码。

之后每个星期都会不定期更新codeforces和atcoder上的题目,想要学习算法的友友们千万别错过了,有什么疑问欢迎大家在评论区留言或者私信博主!

在这里送大家一句话:广积粮,缓称王!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

PH_modest

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

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

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

打赏作者

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

抵扣说明:

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

余额充值