!codeforces 558C Amr and Chemistry-yy题-(位运算相关)

题意:有n个数,每次进行的操作只能是除以2或者乘以2,求这n个数转换成同一个数字所需要的最小的操作步数

分析:

乍一看题目,觉得好难,对于这种每次有两种情况求最后到达的终点的balabala的我就觉得很复杂,这道题说明其实并不可怕,至少有一部分并不可怕。

这道题的做法是暴力枚举出每个数能够走到的所有的数,记录步数,最后找交点输出最小值即可。找交点也不要想复杂了,这n个数都能到达的数就是交点,那么只需要用一个数组记录能到到达这个点的起点个数,最后起点个数等于n的就是交点,这在以每个起点出发枚举的时候就能维护。cnt[i]维护能到点 i 的起点个数,vis[i]维护所有起点到达点 i 的步数和。

说运算相关只是把数写成二进制用左移右移来看这样直观些,其实不用这个也行。最大的交点最多是输入的最大值mx,因为如果要去往大于mx的点必须经过mx这个点那么与最小的步骤数矛盾。

所以解题过程是:扫描每个起点,先一直右移到mx,再回到这个起点开始左移,左移前检查这个点是奇数还是偶数,如果是偶数,继续左移;如果是奇数,那么左移后再右移到mx,然后继续左移。也就是在左移过程中只要遇到一个节点是奇数,就要叉开一条路,让这个奇数先除2再不断的乘以2,但是原本的左移路线一直不变,只是多了一些路线而已,如果不能理解画一下就明白了。

这题还有一些小trick:

1.cnt[a[i]]不能预处理为1,因为有可能有多个起点是一样的即:a[i]=a[j],那么cnt[a[i]]就不应该是1而是2,这个错误是偶然发现的;

2.数组cnt[]和vis[]要开2*10^5,虽然我们推出来终点的极限是输入的最大值mx,mx<10^5,但是由于在算的过程中可能会算到大于mx的数(尽管这对于答案没有贡献),所以数组只开10^5的话,就小了,所以就WA了

代码:

#include<iostream>
#include<cstring>
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
#define INF 1000000007
using namespace std;
int n,a[1000010];
int vis[1000010],cnt[1000010];
int mi,mx;
int main()
{
	cin>>n;
	mx=-1,mi=INF;
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	for(int i=0;i<n;i++){
		cin>>a[i];
		mx=max(mx,a[i]);
	}
//	for(int i=0;i<n;i++) cnt[a[i]]=1;  //错误,不能这么初始化
	for(int i=0;i<n;i++){
		cnt[a[i]]++;
		int tmp=a[i];
		int tot=0;
		while(tmp<=mx){
			tmp*=2;
			tot++;
			cnt[tmp]++;
			vis[tmp]+=tot;
		}
		tmp=a[i];
		tot=0;
		while(tmp>1){
			if((tmp%2==1)&&(tmp!=1)){
				int tmp2=tmp/2;
				int step=tot+1;
				while(tmp2<=mx){
					tmp2*=2;
					step++;
					cnt[tmp2]++;
					vis[tmp2]+=step;
				}
			}
			tmp/=2;
			tot++;
			cnt[tmp]++;
			vis[tmp]+=tot;
		}
	}
	for(int i=1;i<=mx;i++)
		if(cnt[i]==n) mi=min(mi,vis[i]);
	cout<<mi<<endl;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 20:03:22

!codeforces 558C Amr and Chemistry-yy题-(位运算相关)的相关文章

Codeforces 558C Amr and Chemistry(数论+位运算)

C. Amr and Chemistry time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Amr loves Chemistry, and specially doing experiments. He is preparing for a new interesting experiment. Amr has n differ

CodeForces 558C Amr and Chemistry (位运算,数论,规律,枚举)

Codeforces 558C 题意:给n个数字,对每个数字可以进行两种操作:num*2与num/2(向下取整),求:让n个数相等最少需要操作多少次. 分析: 计算每个数的二进制公共前缀. 枚举法亦可. /* *Author : Flint_x *Created Time : 2015-07-22 12:33:11 *File name : whust2_L.cpp */ #include<iostream> #include<sstream> #include<fstrea

暴力 + 贪心 --- Codeforces 558C : Amr and Chemistry

C. Amr and Chemistry Problem's Link: http://codeforces.com/problemset/problem/558/C Mean: 给出n个数,让你通过下面两种操作,把它们转换为同一个数.求最少的操作数. 1.ai = ai*2 2.ai = ai/2 (向下取整) analyse: 基本思路:首先枚举出每个数能够到达的数字并且记录下到达该数组需要的步数,然后从到达次数为n次的数字中选择步数最小的即为答案. 对于一个数字Ai,它可以变换得到的数字可

Codeforces 558C Amr and Chemistry 全都变相等

 题意:给定一个数列,每次操作仅仅能将某个数乘以2或者除以2(向下取整). 求最小的操作次数使得全部的数都变为同样值. 比赛的时候最后没实现.唉.之后才A掉.開始一直在想二分次数,可是半天想不出怎么推断.后来发现事实上每一个数都能变成的数非常少非常少(最多400个不到).于是想到用数学方法+一点暴力,可惜时间不够了. 也不能全然算是数论题.仅仅是用到了一些数学思想.须要一点预处理,后面的计算中还会用到二分的技巧. 对某个数n,设n = s*2^r(当中s为奇数),则n能变成这样一些数:s*2

Codeforces 558C Amr and Chemistry 规律

题目链接 题意: 给定n长的序列 每次可以选一个数 让其 *=2 或者 /=2 问至少操作多少次使得所有数相等. 思路: 对于每个数,计算出这个数可以变成哪些数,以及变成那个数的最小步数. cnt[i] 表示序列中有cnt个数可以变成i step[i] 表示能变成i的 那些数 变成i的花费和是多少. notice: if a[i] == 7, a[i] also can reach 6. by /=2 then *=2 7->3->1.. 3->6->12 1->2->

Codeforces 558C Amr and Chemistry

题意: n个数,每次可以选一个数 让其 *=2 或者 /=2 问至少操作多少次使得所有数相等. 思路: 对于每个数,计算出这个数可以变成哪些数,以及变成那个数的最小步数,用两个数组保存 cnt[i] 表示序列中有cnt个数可以变成i step[i] 表示能变成i的 那些数 变成i的花费和是多少. 其中,遇到奇数的时候要特殊处理一下: 比如,7 可以通过 /2 然后 *2得到6,也就是说任何奇数 i (不包括1)都可以通过2次操作变为 i -1: 代码如下: #include<cstdio> #

位运算相关内容整理

位运算相关内容整理 1) 负数 负数的右移:负数右移的话,由于要保持它是负数,所以负数的二进制的左边补1.如果一直右移的话,最后就就变成0xFFFFFFFF 即-1 如: -4>>1 为-2 :-4>>2为-1 负数的左移:跟正整数左移一样,右边补0.左移总是在低位补零,高位丢失,因而负数左移后可能会变成正数. int x = 0x8fff0000; cout << (x << 1); // 输出为536739840 cout << (-2 &l

bzoj5108 [CodePlus2017]可做题 位运算dp+离散

[CodePlus2017]可做题 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 87  Solved: 63[Submit][Status][Discuss] Description qmqmqm希望给sublinekelzrip出一道可做题.于是他想到了这么一道题目:给一个长度为n的非负整数序列ai,你需 要计算其异或前缀和bi,满足条件b1=a1,bi=bi?1 xor ai(i≥2).但是由于数据生成器出现了问题,他生成的序列a 的长度特

位运算相关

位运算符/移位运算符 运算符 &运算符 操作数1的位 操作数2的位 &的结果位 1 1 1 1 0 0 0 1 0 0 0 0 |运算符 操作数1的位 操作数2的位 &的结果位 1 1 1 1 0 1 0 1 1 0 0 0 ^运算符 操作数1的位 操作数2的位 ^的结果位 1 1 0 1 0 1 0 1 1 0 0 0 ~运算符 操作数的位 ~的结果位 1 0 0 1 移位运算符 原因:主要用于高度优化的代码,在这些代码中,使用其他数据操作的开销太高了 示例: int var1,