zoukankan      html  css  js  c++  java
  • 【计数】hdu5921Binary Indexed Tree

     二进制拆位计算贡献

    题目描述

    树状数组是一种常用的数据结构,下面是树状数组用于给区间 [1,x] 内的数加 t 的代码:

    1 void add(int x,int t){
    2     for (int i=x;i;i-=(i&(-i))){
    3         cnt[i]+=t;
    4     }
    5 }

    当你要给 [l,r] 区间加 x 时,肯定是先给 [1,l1加 x,然后给 [1,r] 加 x

    令 f(l,r) 为:在 cnt 数组一开始为 0 的情况下给区间[l,r]区间加1后,cnt 数组有多少位置不为0。

    现在给定 n,求  sum_{l=1}^{n} sum_{r=1}^{n} f(l,r),对1e9+7取模

    输入格式

    第一行 T

    T 行, 一个 n

    输出格式

    T 行答案

    数据范围

    1n10^18,1T10^4


    题目分析

    这题主流做法是数位dp。不过从hdu5921Binary Indexed Tree学习到一种拆位算贡献的方法

    (但是请注意!上面引用的这篇博客关于f(i),g(i)的分析有写错的地方!)

    本题树状数组求的是后缀和;而∑f(l,r)求的是l-1和r这两个数在依次减去lowbit的过程中不同的数的个数:那么只有当l-1和r变成lcp(l-1,r)时,接下去的数才会相同。

    记g(i)为i二进制中1的个数,所求即

    (公式崩了只能用图片)

    经过上面分析之后,问题相当于转变为求∑g(i)和Σg(lcp(l-1,r))。由于数据范围是10^18级别,显然我们需要log算法。

    将n二进制拆位,考虑从右往左第$i$位的贡献。记[i+1...lens]在十进制下为nxt[i+1],[1...i-1]在十进制下为pre[i-1]。

    1.计算gi第一步:左边的数不改变

    那么为了不大于原数,右边的数应取0...pre[i-1]。

    当ai==1时,有pre[i-1]+1的贡献

    2.计算gi第二步:左边的数可改变

    与上一种情况相对应,左边的数应取0...nxt[i+1]-1.那么此时右边的数可以任意取值,有2^i种取值。

    所以这种情况下,无论ai取值如何,都有nxt[i+1]*2^i的贡献。

    至于还要处理两两lcp的部分,则与之类似。由于是数对之间的负贡献,算的时候再多乘上去就好了。

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 const ll MO = 1000000007;
     4 
     5 int T,bin[103],lens;
     6 ll n,ans,power[103],pre[103],nxt[103];
     7 
     8 ll read()
     9 {
    10     char ch = getchar();
    11     ll num = 0;
    12     bool fl = 0;
    13     for (; !isdigit(ch); ch=getchar())
    14         if (ch=='-') fl = 1;
    15     for (; isdigit(ch); ch=getchar())
    16         num = (num<<1)+(num<<3)+ch-48;
    17     if (fl) num = -num;
    18     return num;
    19 }
    20 int main()
    21 {
    22     T = read(), power[0] = 1;
    23     for (int i=1; i<=60; i++) power[i] = (power[i-1]<<1)%MO;
    24     while (T--)
    25     {
    26         n = read(), ans = lens = 0;
    27         for (ll x=n; x; x>>=1) bin[++lens] = x&1;
    28         pre[0] = nxt[lens+1] = 0;
    29         for (int i=1; i<=lens; i++) pre[i] = (pre[i-1]+power[i-1]*bin[i])%MO;
    30         for (int i=lens; i>=1; i--) nxt[i] = ((nxt[i+1]<<1)+bin[i])%MO;
    31         for (int i=1; i<=lens; i++)
    32         {
    33             if (bin[i]) pre[i-1]++, ans = (ans+pre[i-1])%MO;
    34             ans = (ans+nxt[i+1]*power[i-1]%MO)%MO;
    35         }
    36         ans = (n+1ll)%MO*ans%MO;
    37         for (int i=1; i<=lens; i++)
    38         {
    39             if (bin[i]) ans = (ans+MO-pre[i-1]*pre[i-1]%MO)%MO;
    40             ans = (ans-nxt[i+1]*power[i-1]%MO*power[i-1]%MO+MO)%MO;
    41         }
    42         printf("%lld
    ",ans);
    43     }
    44     return 0;
    45 }

    END

  • 相关阅读:
    PAT Advanced 1067 Sort with Swap(0, i) (25分)
    PAT Advanced 1048 Find Coins (25分)
    PAT Advanced 1060 Are They Equal (25分)
    PAT Advanced 1088 Rational Arithmetic (20分)
    PAT Advanced 1032 Sharing (25分)
    Linux的at命令
    Sublime Text3使用指南
    IntelliJ IDEA创建第一个Groovy工程
    Sublime Text3 安装ftp插件
    Sublime Text3配置Groovy运行环境
  • 原文地址:https://www.cnblogs.com/antiquality/p/9677311.html
Copyright © 2011-2022 走看看