Gym - 101972J - Even Numbers(组合数+规律)

题目:

Yousef loves playing with functions in his free time. Today, he invents the following function:

Yousef will give you a list of queries, and you need to find the answers for them. For each query, you are given an integer n, and your task is to count the number of integers m in which (0 ≤ m ≤ n) and calc(n,  m) is an even number. Can you?

Input

The first line contains an integer T (1 ≤ T ≤ 105) specifying the number of test cases.

Each test case consists of a single line containing an integer n (0 ≤ n ≤ 1018), as described in the problem statement above.

Output

For each test case, print a single line containing the number of integers m in which (0 ≤ m ≤ n) and calc(n,  m) is an even number.

Example

input

2
1
2

output

0
1

题意:给你一个n,要你求出有多少个m(0<=m<=n)使得calc(n,m)为偶数

题解:最开始第一反应就是打表,我输入n,把m=0~n全部枚举一遍,计算对应的calc(n,m)的值,后来发现这是一个组合数公式,满足:C_{n}^{m}\textrm{}=calc(n,m) ,那么这就好办了,对于n,我们只需要在0~n范围内有多少个数满足 C_{n}^{m}\textrm{} 是偶数了,然后百度发现当 n & m == m时  C_{n}^{m}\textrm{} 为奇数,那么将n转换为二进制后,n&m==m与的条件是当n的某一位为1时,m是可以取任意的(0或1),当n的位置为0时,那么m对应位置只能为0才行,因为0与任何数都是0,。所以我们可以知道在n对应为0的位置,m必定为0,这里是固定的,但是在n为1的位置,m是任意取的,因为1与任何一个数最终答案是由最后那个数决定的。所以我们计算出n的二进制中有x个1,那么满足n&m==m的情况就有2的x次方种了,这时候的  C_{n}^{m}\textrm{}  是为奇数的,m总共可取的有n+1种方案(0~n),所以答案就是(n+1)-2^x了,那么先预处理出2的y次方,用数组记录,再直接调用就行了,以下附上队友的代码,感觉自己码力不行啊,根本没想到二进制操作,二进制还是运用的不太熟练,有同样对二进制操作不太熟练的小伙伴可以去看看我的二进制操作总结

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t, n, m, k;
ll a[100];
int main()
{
	a[0] = 1;
	for (int i = 1;i <= 63;i++)
	{
		a[i] = (a[i - 1] << 1);//a[i]=a[i-1]*2,也就是2的x次方
	}
 
	scanf("%lld", &t);
	while (t--)
	{
		scanf("%lld", &n);
		k = n;
		m = 0;
		while (k)
		{
			if (k & 1) m++;//最后一位与1,如果是1就说明找到了一个1
			k >>= 1;//左移一位
		}
		printf("%lld\n", n + 1 - a[m]);
	}
	return 0;
}

下面附上打表代码:

#include<bits/stdc++.h>
using namespace std;
int ans,n;
int check(int i,int j)
{
	if(j<=-1||j>i)return 0;
	if(i==0&&j==0)return 1;
	return check(i-1,j-1)+check(i-1,j);
}
int main()
{
	while(~scanf("%d",&n))
	{
		ans=0;
		cout<<"n:"<<n<<endl;
		for(int i=0;i<=n;i++)
		{
			int k=check(n,i);
			cout<<i<<" "<<k<<endl;
			if(k%2==0)
			{
				
				ans++;
			}
		}
		cout<<"ans:"<<ans<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值