数位DP题目
1、Bit Sequence
题目大意
f(x)定义为x二进制下1的个数,定义一个m位的01串a,看 [0,L] 有多少数字满足对于任意 i 属于 [0, m-1],f(x+i) mod2 = ai。
解题思路
我们发现m很小,所以对于每个i,最多影响一个数后7位1的个数,以及[8, len]中尾部1的个数。所以考虑数位dp。
d
p
[
p
o
s
]
[
s
u
m
]
[
o
n
e
]
[
l
i
m
i
t
]
dp[pos][sum][one][limit]
dp[pos][sum][one][limit]表示,在第pos位,此时[pos+1, len]的1个数奇偶性为sum,尾部1个数奇偶性为one,以及是否是上界的情况limit。我们在dfs的时候,在pos<=6的时候就可以通过暴力判断并返回。
此时1的个数奇偶性有两种情况:
i+j<128: sum+f[i+j]
i+j>=128:sum-one+f[i+j]
f[]数组为预处理的当前数字1的个数的奇偶性。
在二进制下加法和减法和异或相同,所以直接用异或实现。
代码实现
/*
* Filename: d:\test1\test111\test.cpp
* Path: d:\test1\test111
* Created Date: Tuesday, October 11th 2022, 12:47:16 am
* Author: Waitsnow
*
* Copyright (c) 2022 Your Company
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
ll L, m, T;
int a[105];//读入
int b[70];
int f[300]; //预处理0-256中每个数含1的奇偶性
ll dp[65][2][2][2];
ll calc(int sum,int one,int limit)
{
int up = limit ? L % 128 : 127;
ll res = 0;
for (int i = 0; i <= up;i++)
{
bool flag = 1;
for (int j = 0; j < m; j++)
{
if(i+j<128)
{
if((f[i+j]^sum)!=a[j])
{
flag = false;
break;
}
}
else
{
if((f[i+j]^1^sum^(one==1?0:1))!=a[j])
{
flag = false;
break;
}
}
}
if(flag)res++;
}
return res;
}
ll dfs(int cnt,int sum,int one,int limit)
{
ll &v = dp[cnt][sum][one][limit];
if(~v)
return v;
if(cnt<=7)
return calc(sum, one, limit);
int up = limit ? b[cnt] : 1;
ll ans = 0;
for (int i = 0; i <= up;i++)
{
int now = 0;
if(!i)
{
now = 0;
}
else
{
now = one ^ 1;
}
ans += dfs(cnt - 1, sum ^ i, now, limit && i == up);
}
return dp[cnt][sum][one][limit] = ans;
}
ll solve()
{
memset(dp, -1, sizeof dp);
int cnt = 0;
ll temp = L;
while(temp)
{
b[++cnt] = temp&1;
temp >>= 1;
}
return dfs(cnt, 0, 0, 1);
}
int main()
{
cin >> T;
for (int i = 1; i <= 300; i++)
{
f[i] = (f[i >> 1] + (i & 1)) % 2;
}
while (T--)
{
cin >> m >> L;
for (int i = 0; i < m;i++)
cin >> a[i];
cout << solve() << endl; // solve(L)-solve(0);
}
system("pause");
return 0;
}
2、烦人的数学作业
题目背景
NOIP2018初赛结束了,但H还是要上学的啊啊。。上学肯定要完成老师布置的作业,H十分头疼。在如山的作业中,Mr.G布置的数学作业最烦人,H总是完不成~~
题目描述
Mr.G最近在看一些关于数字题的书,他每天愁同学们太聪明了,所有的作业同学们都能做到全对(拿到答案)。Mr.G蒙在鼓里(心知肚明)。为了使同学们进步,Mr.G总是创造一些简单(毒瘤)题来作为作业。以下是数学作业的最后一题题干——
给出一个区间 L L L~ R R R,求 L L L到 R R R区间内每个数的数字和,如123这个数的数字和为1+2+3=6。
( 1 ≤ L ≤ R ≤ 1 0 2 1 \leq L \leq R \leq 10^2 1≤L≤R≤102)
同学们纷纷做出来了,Mr.G一看这最后一题跟摆设没区别了呀,于是他迅速修改了题目,把范围定得非常非常大,且有$ T $组数据,将最终的答案mod $ 10^9+7 $。
(
1
≤
L
≤
R
≤
1
0
18
1 \leq L \leq R \leq 10^{18}
1≤L≤R≤1018)
(
1
≤
T
≤
20
1 \leq T \leq 20
1≤T≤20)
同学们纷纷被难住了。但H为了备战NOIP2018,没有时间完成Mr.G的数学作业~~(其实是不想做QwQ)~~,所以Ta找到了你,希望你帮助Ta和同学完成这烦人的数学作业!
输入格式
输入共 T + 1 T+1 T+1行,
第 1 1 1行读入 T T T。代表有 T T T组数据;
第 2 2 2~ T + 1 T+1 T+1行。读 L i L_i Li和 R i R_i Ri
输出格式
输出共 T T T行,
每行输出 L i L_i Li和 R i R_i Ri的区间数字和mod 1 0 9 + 7 10^9+7 109+7。
提示
对于 50 % 50\% 50% 的数据, 1 ≤ L ≤ R ≤ 1 0 8 1 \leq L \leq R \leq 10^8 1≤L≤R≤108;
对于 100 % 100\% 100% 的数据, 1 ≤ L ≤ R ≤ 1 0 18 1 \leq L \leq R \leq 10^{18} 1≤L≤R≤1018, 1 ≤ T ≤ 20 1 \leq T \leq 20 1≤T≤20。
/*
* Filename: d:\test1\test111\Luogu\P4999.cpp
* Path: d:\test1\test111\Luogu
* Created Date: Friday, October 28th 2022, 10:01:28 am
* Author: Waitsnow
*
* Copyright (c) 2022 Your Company
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 20;
const ll mod = 1e9 + 7;
ll n,m,T;
ll a[N];
ll dp[N][200];
ll dfs(ll cnt,ll sum,ll limit)
{
if(!cnt)
return sum%mod;
if(~dp[cnt][sum]&&!limit)
{
return dp[cnt][sum]%mod;
}
ll up = limit ? a[cnt] : 9;
ll ans = 0;
for (ll i = 0; i <= up;i++)
{
ans = (ans + dfs(cnt - 1, sum + i, limit && i == up)) % mod;
}
if(!limit)
dp[cnt][sum] = ans % mod;
return ans % mod;
}
ll solve(ll n)
{
if(!n)return 0;
ll cnt = 0;
while(n)
{
a[++cnt] = n % 10, n /= 10;
}
return dfs(cnt, 0, 1)%mod;
}
int main()
{
cin >> T;
while(T--)
{
memset(dp, -1, sizeof dp);
cin >> n >> m;
cout <<((solve(m) - solve(n - 1))+mod)%mod << endl;
}
system("pause");
return 0;
}
3、[BalticOI 2013 Day1] Palindrome-Free Numbers
题目描述
我们定义回文串为正着读反着读都一样的数字串,如果一个数字串的一个长度大于 1 1 1 的子串也为回文串的话,那么我们也定义这个数字串为回文串。
顾名思义,不是回文串的数字串为非回文串。
给定 a , b a,b a,b,求区间 [ a , b ] [a,b] [a,b] 中有多少个数字串为非回文串。
输入格式
一行两个整数 a , b a,b a,b 代表这个区间。
输出格式
一行一个整数代表答案。
提示
数据规模与约定
对于 100 % 100\% 100% 的数据, 0 ≤ a ≤ b ≤ 1 0 18 0 \le a \le b \le 10^{18} 0≤a≤b≤1018。
对于其中的 25 % 25\% 25% 的数据, b − a ≤ 1 0 5 b-a \le 10^5 b−a≤105。
/*
* Filename: d:\test1\test111\Luogu\6754.cpp
* Path: d:\test1\test111\Luogu
* Created Date: Friday, October 28th 2022, 2:52:15 pm
* Author: Waitsnow
*
* Copyright (c) 2022 Your Company
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 21;
ll n, m;
ll a[N];
ll dp[N][15][15];
ll dfs(ll cnt, ll limit, ll lead, ll pre1, ll pre2)
{
if (!cnt)
return 1;
if (!lead && !limit && ~dp[cnt][pre1 + 1][pre2 + 1])
{
return dp[cnt][pre1 + 1][pre2 + 1];
}
ll up = limit ? a[cnt] : 9;
ll ans = 0;
for (int i = 0; i <= up; i++)
{
if (i != pre1 && i != pre2)
{
ans += dfs(cnt - 1, limit && i == up, lead && !i, (lead && !i) ? -1 : i, pre1);
}
}
if (!limit && !lead)
dp[cnt][pre1 + 1][pre2 + 1] = ans;
return ans;
}
ll solve(ll n)
{
if (!n)
return 1;
ll cnt = 0;
while (n)
{
a[++cnt] = n % 10, n /= 10;
}
return dfs(cnt, 1, 1, -1, -1);
}
int main()
{
memset(dp, -1, sizeof dp);
cin >> n >> m;
cout << solve(m) - solve(n - 1) << endl;
system("pause");
return 0;
}
4、Sum of Log
分析
因为要满足
i
&
j
=
=
0
i\&j==0
i&j==0,则要求
i
i
i和
j
j
j中没有一位都是1的,那么就证明
(
i
+
j
)
(i+j)
(i+j)是不会进位的;
所以
⌊
l
o
g
(
i
+
j
)
⌋
+
1
\lfloor log(i+j)\rfloor +1
⌊log(i+j)⌋+1表示的就是
i
和
j
中最长的二进制位数
i和j中最长的二进制位数
i和j中最长的二进制位数。
所以考虑到 x , y x,y x,y都在 1 e 9 1e9 1e9并且可以递推,所以我们采取数位DP;
我们用记忆化搜索实现:
其中用全局变量 a n s ans ans保存我们的答案,用 d f s dfs dfs去搜索我们在第 c n t cnt cnt位之后的所有合理方案;
在搜索时,如果无前导0,并且 i ∣ ∣ j i||j i∣∣j,就证明该位就是最高位;
这时,我们把搜出来的方案加起来;
用 a n s = ( a n s + l e n ∗ c n t ) ans=(ans+len*cnt) ans=(ans+len∗cnt)算出对答案的贡献,用 r e s res res计算我们总的方案数;
d f s 中返回方案数, d p [ c n t ] [ l i m i t 1 ] [ l i m i t 2 ] = r e s % m o d dfs中返回方案数,dp[cnt][limit1][limit2]=res \%mod dfs中返回方案数,dp[cnt][limit1][limit2]=res%mod;
代码实现
/*
* Filename: d:\test1\test111\Codeforces\SumofLog.cpp
* Path: d:\test1\test111\Codeforces
* Created Date: Friday, October 28th 2022, 9:34:03 pm
* Author: Waitsnow
*
* Copyright (c) 2022 Your Company
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f;
const ll N = 35;
const ll mod = 1e9 + 7;
ll n,m,T;
ll a[N], b[N];
ll dp[N][2][2];
ll ans;
ll dfs(ll cnt, ll limit1, ll limit2,ll lead)
{
ll &v = dp[cnt][limit1][limit2];
if(!cnt)
return 1;
if(~v)
return v%mod;
ll up1 = limit1 ? a[cnt] : 1;
ll up2 = limit2 ? b[cnt] : 1;
ll len=0,res=0;
for (ll i = 0; i <= up1;i++)
{
for (ll j = 0; j <= up2;j++)
{
if(i&j)
continue;
ll temp = dfs(cnt - 1, limit1 && i == up1, limit2 && j == up2, lead && !i && !j)%mod;
res = (res + temp) % mod;
if(lead&&(i||j))
{
len = (len + temp) % mod;
}
}
}
ans = (ans + len * cnt) % mod;
return dp[cnt][limit1][limit2] = res%mod;
}
ll solve()
{
ans = 0;
memset(a, 0, sizeof a);
memset(b, 0, sizeof b);
memset(dp, -1, sizeof dp);
ll cnt1 = 0, cnt2 = 0;
while(n)
{
a[++cnt1] = n & 1, n >>= 1;
}
while(m)
{
b[++cnt2] = m & 1, m >>= 1;
}
dfs(max(cnt1, cnt2), 1, 1,1);
return ans%mod;
}
int main()
{
cin >> T;
while(T--)
{
cin >> n >> m;
cout << (solve()+mod)%mod << endl;
}
system("pause");
return 0;
}