链接:http://cstest.scu.edu.cn/soj/problem.action?id=4120
题目大意:定义一个数集的价值是:数集中满足以下要求的数对的个数
x,y (x&y)==min(x,y)
现在给你一个N,N保证是2的T次方 且 1<T<23
你要求出,一个含有1-N N个数的数集,去掉其中的几个数,能使数集的价值变成0?
TAG:数学
当初刚看这一题的时候完全木了。。就在各种猜方法。。。然后各种WA。。。
首先那个公式的意义就是。。
两个二进制数。。
大的那一个,在小的那一个为1的位上不能全是1= =【好难表述。。
然后我想出了一个N=16的时候的答案。。可以只用去掉9个。。剩下的是
10000
1100
1010
1001
110
101
11
从这个就可以看出一些端倪。。
首先最大的肯定是可以留下的。。因为开头的1下面都没有。。后面又全是0
然后后面的4个0,我要往里面填1,然后填的1不重复。。而且这里填的1的个数全部一样是最优的【忘记怎么证明了额。。。
4个格子,肯定是填2个1的方法最多。。
即C(4,2)=6
加上上面的一个。。就是7个。。
答案就是N-C(k,2*k)-1
k要自己算出来。。C就预处理一下就行了。。
所以程序还是很短的。。
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int c[30][30]={0};
void init()
{
c[0][0]=1;
for(int i=1;i<30;i++)
{
c[i][0]=1;
c[i][i]=1;
}
for(int i=2;i<30;i++)
for(int j=1;j<i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
init();
while(t--)
{
int n;
scanf("%d",&n);
int count=0;
int tt=n;
while((n&1)==0)
{
count++;
n/=2;
}
printf("%d\n",tt-c[count][count/2]-1);
}
return 0;
}