您的位置:首页 > 其它

[数位DP] ZROI 2017 提高3 T1 树状数组

2017-10-07 12:54 204 查看
不错的题。这个应该从二进制角度考虑。我们知道树状数组每次就是把二进制最末尾的 1 去掉。

那么考虑对 r 和 l−1 分别加 1 减 1 后,有值的节点个数即 cnt(r)+cnt(l−1)−2∗cnt(prefix(r,l−1)) 。其中cnt 是二进制下 1 的个数,prefix 是公共前缀。这个不难得到。

然后就是如何计算总贡献了:

∑0≤x<y≤ncnt(x)+cnt(y)−2∗cnt(prefix(x,y))

这可以简单的数位 DP 得到:f[i][0/1][0/1] 表示填了前 i 位,x 是否已小于y , y 是否已小于 n ,的贡献和。还要记个 g[i][0/1][0/1] 表示方案数。就可以转移了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD=1e9+7;
typedef long long LL;
int _test,f[70][2][2],g[70][2][2],a[70]; //f[i][0:L==R 1:L<R ][0:R==n 1:R<n ]
LL n;
int Solve(){
memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); a[0]=0;
LL _n=n; do a[++a[0]]=_n&1, _n>>=1; while(_n);
g[a[0]+1][0][0]=1;
for(int i=a[0]+1;i>=2;i--)
for(int j1=0;j1<=1;j1++)
for(int j2=0;j2<=1;j2++) if(g[i][j1][j2]){
for(int r=0;r<=(j2?1:a[i-1]);r++)
for(int l=0;l<=(j1?1:r);l++){
(f[i-1][j1|(l<r)][j2|(r<a[i-1])]+=(f[i][j1][j2]+(LL)g[i][j1][j2]*(l+r-(l&&r&&!j1)*2))%MOD)%=MOD;
(g[i-1][j1|(l<r)][j2|(r<a[i-1])]+=g[i][j1][j2])%=MOD;
}
}
return (f[1][1][0]+f[1][1][1])%MOD;
}
int main(){
scanf("%d",&_test);
while(_test--){
scanf("%lld",&n);
printf("%d\n",Solve());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: