HDU - 6304 Chiaki Sequence Revisited

题目链接

题意:给出了个计算a[i]的公式,然后让你求a[1~n]的和。

分析:好的,我们简单粗暴的先打个表,发现规律(个屁),每一个a[i]出现的次数是lowbit(a[i])长度次,比如16出现的次数是lowbit(16) = 10000,长度为5次(我也不知道这是什么神奇的东西),接下来就可以写代码了,每一部分可以等差数列求和搞,一些细节会在下面的代码里面注释。
这里写图片描述

更正:二分check那里,应该是x=a[i],算出对应的i。

(参考了vj上一位不认识大佬“zstu_LLY”的代码,不然可能都这题都补不了)

#include 
#define mst(a,b) memset(a,b,sizeof(a))
#define ALL(x) x.begin(),x.end()
#define pii pair
#define eps 1e-4
inline long long lowbit(long long x){return x&-x;}
const int N = 1e6+10;
const long long mod = (long long)1e9+7;
const int INF=0x3f3f3f3f;
const long long LINF=(1LL<<62);
typedef long long LL;
typedef unsigned long long ULL;
//#define LOCAL
const double PI = acos(-1.0);
using namespace std;

LL n,inv2,p[63];

LL q_pow(LL base, LL b){
  LL res = 1;
  while(b){
    if(b&1) res = res*base%mod;
    base = base*base%mod;
    b>>=1;
  }
  return res;
}
bool check(LL x){   //为什么要这么写呢,看上面打的表,除以每个2^i就可以得出2^i有多少个,这样子加完就得到对应的a[x]是多少。
  LL temp = 0;
  for(int i = 0; i <= 62; i++){
    temp += x/p[i];
    if(temp >= n-1) return true;
  }
  return temp >= n-1;
}
LL cal(LL x){
  LL temp = 0;
  for(int i = 0; i <= 62; i++) temp += x/p[i];
  return temp;
}
int main(){
#ifdef LOCAL
    freopen("out.txt", "w", stdout);
#endif // LOCAL
    inv2 = q_pow(2,mod-2);  //2的逆元,等下等差数列求和的时候要用
    p[0] = 1;
    for(int i = 1; i <= 62; i++) p[i] = p[i-1]*2; //预处理2^i
    int T; scanf("%d",&T);
    while(T--){
      scanf("%lld",&n);
      if(n==1){
        printf("1\n");
        continue;
      }
      LL l = max(1LL,n/2-30), r = min(n,n/2+30), mid, pos;
      while(l<=r){
        mid = (l+r)>>1;
        if(check(mid)){   //二分找n对应的a[n];
          pos = mid;      //pos即为a[n]
          r = mid-1;
        }else l = mid+1;
      }
      LL res = pos-1, now = 0;  //这里pos-1是因为在a[n]位置的n可能不是刚好符合2^i,所以要找前面一个,前面一个必定是符合2^i个的。
      for(int i = 0; i <= 62; i++){
        if(p[i]>res) break;
        LL temp = res/p[i];
        temp %= mod;
        now = (now+p[i]%mod*(temp+1)%mod*temp%mod*inv2%mod)%mod;  //等差数列求和,不要忘记Mod
      }
      now = (now+(n-1-cal(res)%mod)%mod*pos%mod)%mod;  //剩下这里就是那些不完整的a[n]了
      printf("%lld\n",(1+now)%mod);
    }
    return 0;
}

ps:dls说这场多校,4+铜,6+银,7+金的样子,所以尽量补到6题吧,而这道题通过的人又蜜汁的多orz,就不得不补了。在看过一份又一份天书一样的代码之后终于看到一份比较友好的,让补题成为可能。再次感谢vj上shared这道题的大佬。

你可能感兴趣的:(规律)