ZROJ87 树状数组 - 数位dp -

题目链接:http://zhengruioi.com/problem/87

题解:
首先考虑 f(l,r) 代表什么,官方题解很详细了就不再赘述了:

因此我们要求的就是对于所有 l,rl1r 的最长公共前缀的 1 的个数,记为 S
容易发现答案就是所有 (l1,r),1lrn 的二进制下 1 的个数和,再减去 2S
形式化的:ans=l=1nr=ln(F(l1)+F(r))2SF 代表 popcount

先考虑一下 S 怎么求,可以用数位 dp 解决
f(i,0/1,0/1) 代表考虑到二进制下第 i 位,l==r, r==n 时的最长公共前缀下 1 的个数,g(i,0/1,0/1) 同理,代表方案数
每次转移的时候就枚举 l,r 的当前位。如果之前有 l==r 并且当前位两个都是 1 的话,最长公共前缀 +1,因此当前 dp 值需要加上 g(i+1,1,0/1) (方案数)
还有一点需要注意,因为实际上要求的区间最接近也是 [r1,r],因此不存在 [r,r] 的区间,因此 dfs 末尾的时候需要判断如果相等的话方案数应该设为 0
至于前面和式的求法,首先注意到只需要算 1..n 的 popcount 个数,然后再 ×n 即可。采用相同的 dp 方式(设一个 dp[x][0/1] 表示popcount数,dpp表示方案数)

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, mod=1e9+7;

ll n;
int bit[65],cnt;
int f[65][2][2], g[65][2][2], dp[65][2], dpp[65][2];

pii dfs0(int x,int isup){
	if(x == cnt+1)return mpr(0, 1);
	int &dd = dp[x][isup];
	if(~dd)return mpr(dd, dpp[x][isup]);
	
	dd = 0;
	int &ng = dpp[x][isup];ng = 0;
	for(int i=0;i<=(isup ? bit[x] : 1);i++){
		pii now = dfs0(x+1, isup && i == bit[x]);
		(dd += now.first) %= mod;
		if(i == 1)(dd += now.second) %= mod;
		(ng += now.second) %= mod;
	}
	return mpr(dd, ng);
}

pii dfs(int x,int iseq,int isup){
	if(x == cnt+1){
		if(!iseq)return mpr(0,1);
		else return mpr(0,0);
	}
	int &dd = f[x][iseq][isup];
	if(~dd)return mpr(dd, g[x][iseq][isup]);
	
	int up = 1;if(isup)up = bit[x];
	int &ng = g[x][iseq][isup];
	dd = ng = 0;
	for(int r=0;r<=up;r++)
		for(int l=0;l<=(iseq?r:1);l++){
			pii now = dfs(x+1, iseq && l==r, isup && r == up);
			(dd += now.first) %= mod;
			if(l==1&&r==1&&iseq)(dd += now.second) %= mod;
			(ng += now.second) %= mod;
		}
	return mpr(dd, ng);
}

void solve(){
	memset(f,-1,sizeof f);
	memset(g,-1,sizeof g);
	memset(dp,-1,sizeof dp);
	memset(dpp,-1, sizeof dpp);
	cin >> n;
	ll t = n;
	cnt = 0;
	while(t){
		bit[++ cnt] = t&1;
		t >>= 1;
	}
	reverse(bit+1,bit+cnt+1);
	pii res0 = dfs0(1, 1);
	pii res = dfs(1, 1, 1);
	cout << (1ll*res0.first*(n%mod)%mod - 2ll*res.first%mod + mod) % mod << '\n';
}

signed main(){
	int te;scanf("%d",&te);
	while(te--)solve();

	return 0;
}

posted @   SkyRainWind  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示