题目链接:
http://codeforces.com/problemset/problem/325/B
题意:
给一个long long范围的n,问存在哪些数满足这个要求:设初始sum值为0,对于某个数k,无限除二直至它是一个奇数,在这个过程中sum值加上每次除二得数,并加上最后奇数x的一个值x * (x-1) / 2。
思路:
容易知道最后的x在n内且最大值略比n的开方大些。假设原始k值需要除n次2才能得到x,于是又,其中n为0的情况需要讨论下。
血的教训再次证明多项式在ACM中用二分穷举答案是最靠谱的。注意添加退出条件,注意计算x*2的n次方时会爆long long,因此枚举x的上界需要随n的增大做调整减小。
源码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL MAXN = 1e18+1;
LL out[100];
int cnt;
LL up;
LL cal(LL t1,LL x)
{
return x * (x - 1) / 2 + (t1 - 1) * x;
}
LL fi(LL n, LL t1)
{
LL ri,le;
le = 0;
ri = up;
while(ri - le > 1){
LL mid = (ri + le) / 2;
// printf("cal(le) = %I64d,cal(ri) = %I64d,cal(mid) = %I64d\n",cal(t1,le),cal(t1,ri),cal(t1,mid));
// printf("le = %I64d,ri = %I64d\n",le,ri);
// system("pause");
if(cal(t1,mid) == n){
// printf("t1 = %I64d\n",t1);
// printf("mid = %I64d,cal(mid) = %I64d\n",mid,cal(t1,mid));
// system("pause");
return mid;
}
else if(cal(t1, mid) > n)
ri = mid;
else if(cal(t1,mid) < n)
le = mid;
}
// printf("t = %I64d,le = %I64d,ri = %I64d\n",t1,le,ri);
// printf("cal(le) = %I64d,cal(ri) = %I64d\n",cal(t1,le),cal(t1,ri));
// system("pause");
up = ri;
if(cal(t1,ri) == n)
return ri;
else if(cal(t1,le) == n)
return le;
else if(ri==1 && le == 0)
return 0;
return -1;
}
int main()
{
LL n;
while(scanf("%I64d",&n) != EOF){
cnt = 0;
up = sqrt(4 * n);
// if((sqrt(1 + 8 * n) * sqrt(1 + 8 * n) == 1 + 8 * n) && ((1 + sqrt(1+8*n)) % 2 == 0))
// out[cnt++] = (1 + sqrt(1 + 8 * n)) / 2;
LL t1 = 1;
while(1){
LL temp = fi(n, t1);
// printf("temp = %I64d\n",temp);
// system("pause");
if(temp == 0)
break;
else if(temp != -1 && temp%2 != 0)
out[cnt++] = temp * t1;
t1 *= 2;
}
sort(out, out+cnt);
if(cnt > 0){
for(int i=0; i<cnt; i++){
if(i>0 && out[i] == out[i-1])
continue;
printf("%I64d\n",out[i]);
}
}
else
printf("-1\n");
}
return 0;
}