題意搬運:
用樹狀數組維護一個序列,在給區間
[l,r] 加上一個t 的時候,要給[1,r] 加上t ,給[1,l−1] 減去t ,兩次操作後值真正發生變化的節點個數就是這一次區間修改的代價,現在要修改每一個[1,n] 的子區間,求總代價對109+7 取模後的結果。
題解搬運
記
cnti 表示i 的二進制表達式中1 的個數,lcp(i,j) 表示i 和j 的二進制表示的最長公共前綴(如果長度不同,右對齊之後在左邊補0),那麼就是求考慮逐位計算貢獻,對於第∑r=1n∑l=0r−1(cntl+cntr−2cntlcp(l,r))=12∑l=0n∑r=0n(cntl+cntr−2cntlcp(l,r)) i 位,枚舉兩個數l 和r ,如果前i 位都相同那麼沒有貢獻,否則貢獻是l 和r 在第i 位的數之和,於是dp[i][0/1][0/1][0/1] 表示已經考慮了最高i 位,現在l 和r 的前綴是否相等以及各自是否等於n 的前綴的方案數,計算貢獻時要補上後綴,觀察到l 和r 的後綴是相互獨立的,只需考慮前綴是否等於n 的前綴。
其實dp數組就是前i位是否開始統計,l和r是否卡到上界的狀態
每次統計的時候,當前位的能產生的貢獻次數就是後面能接的後綴的個數
#include<bits/stdc++.h>
using namespace std;
const int maxn = 63,mod = 1e9+7;
#define LL long long
int inser(int *s,LL *v,LL x){
memset(s,0,sizeof(int)*maxn);
int len = 0;
while(x)
s[len++] = x & 1, x >>= 1;
v[0] = s[0];
for(int i=1;i<len;i++)
v[i] = v[i-1]+((1ll*s[i])<<i);
for(int i=0;i<len;i++)
v[i] = (v[i] + 1) % mod;
return len;
}
LL dp[maxn][2][2][2], lr[maxn];
int lrs[maxn];
LL tim(int pos,bool lnd,bool rnd){
if(pos < 0) return 1;
LL full = (1ll << (pos+1)) % mod, ret = 1;
if(lnd) (ret *= lr[pos]) %= mod;
else (ret *= full) %= mod;
if(rnd) (ret *= lr[pos]) %= mod;
else (ret *= full) %= mod;
return ret;
}
LL dfs(int pos,bool lbnd,bool rbnd,bool scnt){
if(pos < 0) return 0;
LL & ndp = dp[pos][lbnd][rbnd][scnt];
if(ndp != -1) return ndp;
int lb = lbnd ? lrs[pos] : 1, rb = rbnd ? lrs[pos] : 1;
ndp = 0;
for(int i=0;i<=lb;i++){
for(int j=0;j<=rb;j++){
(ndp += dfs(pos-1
,lbnd && i == lb
,rbnd && j == rb
,scnt || (i != j)) ) %= mod;
(ndp += (scnt||(i!=j))*(i+j)
* tim(pos-1
,lbnd && i == lb
,rbnd && j == rb) ) %= mod;
}
}
return ndp;
}
LL cal(LL num){
memset(dp,-1,sizeof(dp));
int n = inser(lrs,lr,num);
return dfs(n-1,true,true,false);
}
int main(){
int T,icase = 1;
scanf("%d",&T);
LL n;
memset(dp,-1,sizeof(dp));
while(T-- && ~scanf("%lld",&n)){
LL rev = (mod + 1) / 2;
LL ans = cal(n)* rev % mod;
printf("Case #%d: %lld\n",icase++,ans);
}
return 0;
}