2018.08.19 NOIP模拟 number(类数位dp)

Number

题目背景

SOURCE:NOIP2015-SHY-10

题目描述

如果一个数能够表示成两两不同的 3 的幂次的和,就说这个数是好的。
比如 13 是好的,因为 13 = 9 + 3 + 1 。
又比如 90 是好的,因为 90 = 81 + 9 。
现在我们用 a[i] 表示第 i 小的好数。
比如 a[1] = 1, a[2] = 3, a[5] = 10 。
给定 L,R,请求出 a[L] 到 a[R] 的 和 mod 232。

输入格式

第一行一个整数 T,表示数据组数。
接下来 T 行,每行两个整数 L,R 表示一组询问。

输出格式

输出 T 行,每行为一个整数,表示相应询问的答案。

样例数据 1

输入

5
1 3
3 3
4 5
6 7
2 5

输出

8
4
19
25
26

备注

【数据范围】

对 30% 的输入数据:1≤T≤100;R≤1000 。
对 100% 的输入数据:1≤T≤100000;1≤L≤R≤10^18 。

考试的时候正解没调出来,只能交30分暴力233。。。
其实就是类似于二进制倍增的方法来处理一波三进制啊。。。
事实上我们只需要统计每个三进制位出现的次数就可以求得答案。
这让我们想到最高位在取与不取的时候会影响地位低位的情况,因此我们可以像数位dp那样讨论最高位的情况。

代码:

#include<bits/stdc++.h>
#define uint unsigned int
#define ll long long
using namespace std;
inline ll read(){ll ans=0;char ch=getchar();while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();return ans;}
inline void write(uint x){if(x>9)write(x/10);putchar((x%10)^48);}
int T;
uint mul[64],sum[64];
inline void init(){mul[0]=1,sum[0]=1;for(int i=1;i<=63;++i)mul[i]=mul[i-1]*3,sum[i]=sum[i-1]+mul[i];}
inline uint calc(ll x){
    uint ret=0,tmp=0;for(int i=63;i;--i)if(((1ll<<i)&x))ret+=sum[i-1]*(1ll<<(i-1)),ret+=tmp*(1ll<<i),ret+=mul[i],tmp+=mul[i];
    if(x&1)ret+=tmp+1;return ret;
}
int main(){
    T=read(),init();
    while(T--){ll a=read(),b=read();write(calc(b)-calc(a-1)),puts("");}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/81838882