Hdu-5921 Binary Indexed Tree(数位DP)


Problem Description
Recently, Mr. Frog has learned binary indexed tree. Here is the code of adding t to the interval [1,x]:
    void add (int x, int t ){
        for (int i = x; i != 0; i -= i & (-i))
            a[i] += t;
    }


If Mr. Frog is required to add t to the interval [l,r], he will add(r,t), and then add(l - 1,-t).

The cost of an interval [l,r] is defined as the number of the “really changed point”. The “really changed point” is the point whose value is modified by the given code.

For example, in order to add 1 to the interval [6,6], Mr. Frog will add 1 to the interval [1,6] (a[6] and a[4] will be added by 1), and add -1 to the interval [1,5] (a[5] and a[4] will be added by -1).

As the result, a[6] will be added by 1, a[5] will be added by -1, and a[4] will be added by 0. a[6] and a[5] are “really changed point”, and the cost is 2.

Mr. Frog wants to calculate the sum of the cost of the interval [l,r] [1,n] where l and r are two integers. Help Mr. Frog solve the problem.
 

Input
The first line contains only one integer T ( T10000 ), which indicates the number of test cases.

For each test case, it contains an integer n ( 1n1018 ).
 

Output
For each test case, output one line ”Case #x: y”, where x is the case number (starting from 1), y is the sum of the cost (modulo 1000000007).
 

Sample Input
  
  
3 1 2 3
 

Sample Output
  
  
Case #1: 1 Case #2: 4 Case #3: 10
 

Source
 

Recommend

wange2014


题意略.

分析:我们把这棵树画出来,然后考虑分别计算树上每个节点对答案的贡献,很容易发现每个节点对答案的贡献为 子树个数*(n-子树个数+1),只和子树个数相关,所以我们只要分别计算出子树个数为2^k的树有多少个就行了,这个很容易用数位dp实现,但是这样直接统计计算的答案是不正确的,因为最大节点编号的限制那些编号特别大的子节点是不能算入答案的,所以我们把这种边界情况单独计算就可以了。


#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<queue>
#define INF 2147483640
#define eps 1e-9
#define MAXN 30010
#define MOD 1000000007
typedef long long ll;  
using namespace std;
int t,pos,j,a[65];
ll n,dp[65][2];
ll dfs(int pos,int limit)
{
    if(pos == j) return !limit;
    if(dp[pos][limit] >= 0) return dp[pos][limit];
    int up = limit ? a[pos] : 1;
    ll ans = 0;
    for(int i = 0;i <= up;i++) 
    {
        ans += dfs(pos-1,limit && i == up);
        if(ans >= MOD) ans %= MOD;
    } 
    dp[pos][limit] = ans;
    return ans;
}
void solve(ll x)
{
    pos = 0;
    while(x)
    {
        a[pos++] = x & 1;
        x >>= 1;
    }
}
int main()
{
    scanf("%d",&t);
    for(int T = 1;T <= t;T++)
    {
        scanf("%I64d",&n);
        solve(n);
        ll ans = 0;
        for(j = 0;(1ll<<j) <= n;j++)
        {
            memset(dp,-1,sizeof(dp));
            ans = (ans + ((dfs(pos-1,1)*((1ll<<j) % MOD) % MOD)*((n + 1 - (1ll<<j)) % MOD)) % MOD) % MOD;
            if(a[j])
			{
				ll temp = (n - ((n>>j)<<j) + 1) % MOD;
				ans = (ans + (temp*((n + 1 - temp) % MOD)) % MOD) % MOD;
			} 
        } 
        printf("Case #%d: %I64d\n",T,ans);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值