2020CCPC长春 D. Meaningless Sequence(打表/数位DP)
Once there was a mathematician, who was obsessed with meaningless number sequences. Here is one of them.
𝑎𝑛={1,𝑐⋅max0≤𝑖<𝑛𝑎𝑛&𝑖,𝑛=0otherwise,an={1,n=0c⋅max0≤i<nan&i,otherwise,
where && denotes the bitwise AND operation.
As a mathematician, he could easily tell what 𝑎𝑛an was for any 𝑛n, but he wanted to test you. You are required to tell him
(∑𝑖=0𝑛𝑎𝑖)mod(109+7)(∑i=0nai)mod(109+7)
to convince him that you have a deep understanding of this (although meaningless) sequence.
先打一个表找规律,发现其实很难看出来。考虑到规律可能与c的取值无关,于是打印一下每个位置n对应的最大的\(a_{n \& i}\)所在的位置,发现这个位置实际上就是n去掉最高位的1,于是每个位置n对应的值实际上就是\(c^{n的二进制表示中1的个数}\)。由于答案要求所有数的和,且注意到位与位之间彼此无关,可以考虑数位dp求解。设dp[i, j, k]表示当前遍历到len,且当前位为j,是否紧贴边界为k的答案。直接套板子即可。
#include <iostream>
#define ll long long
#define mod 1000000007
using namespace std;
//string n;
ll c;
string n;
ll fpow(ll a, ll b) {
ll ans = 1;
for(; b; b >>= 1) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
}
return ans;
}
ll get(ll x) {
int ans = 0;
while(x) {
if(x & 1) ans++;
x >>= 1;
}
return ans;
}
ll dp[3005][2][2];
bool num[3005];
int nn = 0;
ll dfs(ll len, bool pre, bool tight) {
if(dp[len][pre][tight]) return dp[len][pre][tight];
if(len == 0) return 1;
ll ans = 0;
for(ll i = 0; i < 2; i++) {
if(i == 0) ans = (ans + dfs(len - 1, 0, tight && (i == num[len]))) % mod;
else {//这一位如果是1的话,上一位肯定不能是1,同时要注意不能超边界
if(i > num[len] && tight) continue;
ans = (ans + c * dfs(len - 1, 1, tight && (i == num[len])) % mod) % mod;
}
}
return dp[len][pre][tight] = (ans % mod);
}
int main() {
cin >> n >> c;
for(int i = n.size() - 1; i >= 0; i--) {
if(n[i] == '0') num[++nn] = 0;
else num[++nn] = 1;
}
//从0到n c的每个数中1的个数次方
cout << dfs(nn, 0, 1) % mod;
return 0;
}