[2020 ICPC 上海 C] Sum of Log
https://ac.nowcoder.com/acm/contest/9925/C
题意:
在\(10^5\)组数据中,每组给定两个非负整数\(X,Y(X,Y \leq 10^9)\)求\(\sum_{i = 0}^X\sum_{j=[i=0]}^Y[i\&j=0][log_2(i+j)+1]\)
思路:
很容易想到在二进制下进行数位\(dp\),传递枚举到的位置,第一个出现\(1\)的位置,以及\(X\)和\(Y\)是否达到上边界。但是这样时间复杂度达到了\(O(T * 30 * 30 * 2 * 2)\),虽然只要初始化得好的话牛客上抖一抖和CF的机子上能刚好跑过去,但是时间复杂度是可以优化的。我们发现当第一个出现\(1\)的位置在前面确定之后,后面所有数字能产生的贡献都是一样的,所以如果我们把dp数组里传递的值从产生的贡献改成能产生贡献的数量,就可以把时间复杂度降到\(O(T * 30 * 2 * 2 * 2)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <class T> inline void read(T &x) {
int f = 0; x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if (f) x = -x;
}
const int N = 32;
const ll mod = 1e9 + 7;
int dp[N][2][2][2];
int tot1, tot2, dig1[N], dig2[N];
int x, y, ans;
void add(int &a, int b) {
a += b;
if (a >= mod) a -= mod;
}
int dfs(int pos, bool lim1, bool lim2, bool ok) {
if (pos == -1) return ok;
if (dp[pos][lim1][lim2][ok]) return dp[pos][lim1][lim2][ok];
int to1 = lim1? dig1[pos] : 1;
int to2 = lim2? dig2[pos] : 1;
int tmp = 0;
for (int i = 0; i <= to1; ++i) {
for (int j = 0; j <= to2; ++j) {
if (i && j) continue;
int num = dfs(pos - 1, lim1 && i == to1, lim2 && j == to2, ok || i || j);
add(tmp, num);
if (!ok && (i || j)) add(ans, (ll)num * (ll)(pos + 1) % mod);
}
}
return dp[pos][lim1][lim2][ok] = tmp;
}
void calc(int x, int y) {
int len = 0;
memset(dig1, 0, sizeof(dig1));
memset(dig2, 0, sizeof(dig2));
while (x) {
dig1[len++] = x & 1;
x >>= 1;
}
len = 0;
while (y) {
dig2[len++] = y & 1;
y >>= 1;
}
dfs(30, true, true, false);
}
void solve() {
ans = 0;
memset(dp, 0, sizeof(dp));
scanf("%d %d", &x, &y);
calc(x, y);
printf("%d\n", ans);
}
int main() {
int t = 1;
scanf("%d", &t);
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号