HDU 4321 Arcane Numbers 2 按位处理, 想法计数题

来源:互联网 发布:淘宝网苹果6手机价格 编辑:程序博客网 时间:2024/06/05 09:06

题目大意:

就是对于给出的A, B, N, A <= 1e4, B <= 1e16, N <= 1e12, 统计在B + A, B + 2*A, B + 3*A, B + 4*A.... B + N*A这N个数的二进制表示法下一共有多少个1


大致思路:

这个题调了好久...想法虽然可行但是写错了几个细节的位置...于是调了半个小时才调过....感觉思路明确了复杂度也就清楚了

思路写在代码注释里了


代码如下:

Result  :  Accepted     Memory  :  1620 KB     Time  :  140 ms

/* * Author: Gatevin * Created Time:  2015/8/11 23:31:36 * File Name: Sakura_Chiyo.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;/* * 首先要求的是B + A, B + 2*A, .... B + N*A这N个数的二进制中总共有多少个1 * 那么我们按位一步一步来, 先统计N个数中第K位是1的个数 * 第K位按照二进制从右向左数, 从1开始数 * 那么不难发现对于第K位B + T*A 和 B + T*A + (1 << K)*A一定是一样的 * 那么我们在计算第K位的时候对于 1 <= T <= (1 << K) - 1计算即可 * 对于B + i*A, 1 <= i <= (1 << K) - 1相同的贡献个数为 (N - i)/(1 << K) + 1个这里是整数除法 * 但是由于K可以比较大, 所以对于第K位不能暴力枚举所有可能的T * 那么当(1 << K) <= A的时候我们暴力枚举 * 当(1 << K) > A时, 我们考虑这样一个事实: * 不难发现如果当前第i项 B + i*A对于(1 << K)取模之后剩下的部分, 在接下来添加A的过程中会有连续的一段不向第K位二进制进位 * 那么就会出现连续的B + i*A, B + (i + 1)*A,...., B + j*A一整段的第K位都是0, 然后又连续一段是1, 两者交替的情况 * 于是这样我们只需要计算连续的一段多长就可以了, 由于(1 << K) > A, 前面一段对(1 << K)取模剩余res的时候接下来一段长度是((1 << K) - res - 1)/A * 那么枚举第K位的时间复杂度是O(log(B + A*N))而每一次枚举, 需要计算区间[1, (1<<K) - 1], 一段区间计算次数是大约是(1<<K)/(((1 << K) - res - 1)/A)次 * 而(1 << K) / (((1 << K) - res - 1)/A)也就是是O(A)的级别 * 于是整体复杂度也就是O(log(B + A*N)*A) */int T;lint A, B, N;lint solve(){    lint ans = 0;    lint mx = B + A*N;    for(int K = 1; (1LL << (K - 1)) <= mx; K++)//从右向左第K位    {        lint L = 1LL << (K - 1);        lint S = 1LL << K;        lint rest = (B + A) % L;        int now = (B + A) & L ? 1 : 0;//当前第K位是0还是1        lint T = 1;        while(T <= S && T <= N)        {            lint step = (L - rest - 1) / A;//接下来会有的连续的now(0 or 1)的个数            step = min(step, min(N - T, S - T));                        //也就是说[T, T + step]都是now            //一共step + 1个, 对答案的贡献是sigma((N - i) / (1 << K)) T <= i <= T + step            //cout<<L<<" "<<rest<<" "<<now<<" "<<T<<" "<<step<<" "<<K<<" "<<ans<<endl;            //getchar();            if((N - T) / S + 1 == (N - (T + step))/S + 1) ans += (step + 1)*((N - T)/S + 1) * now;            else            {                lint mid = (N - T) / S * S;//[mid, N - T]这一段都是相同的, [N - (T + step), mid - 1]是相同的 (相同指的是B + (T + (1 << K))*A的个数相同)                ans += ((N - T) / S + 1) * (N - T - mid + 1) * now;                ans += ((N - (T + step)) / S + 1) * (mid + T + step - N) * now;            }            rest = (rest + (step + 1)*A) % L;            T += step + 1;            now = (B + T*A) & L ? 1 : 0;//接下来是相反的连续段        }    }    return ans;}int main(){    scanf("%d", &T);    for(int cas = 1; cas <= T; cas++)    {        scanf("%I64d %I64d %I64d", &A, &B, &N);        printf("Case #%d: %I64d\n", cas, solve());    }    return 0;}


0 0
原创粉丝点击