AtCoder Regular Contest 066
C 是一眼题,F 一眼分治,但是我不会斜率优化 DP,就不写了,NOIp 结束以后一定要去补这块芝士。
D - Xor Sum
给定一个数 \(n\),求点对 \((u,v)(u,v\in [0,n])\) 的数量,满足:
存在一对非负整数 \(a,b\),使得 \(a\operatorname{xor} b =u,a\operatorname{and}b=v\)
\(1\le n\le 10^{18}\)
首先要知道一个性质:\(a+b=a\operatorname{xor}b+2(a\operatorname{and}b)\)(虽然这个解法用不到qwq)
\(n\le 10^{18}\) 的范围明显是要 polylog 算法,那么可以考虑往折半想。
统计方案,还是用 DP,由于 \(a\operatorname{xor}b\le a+b\),故设 \(f(x)\) 表示 \(a+b=x\) 的所有可能的 \(a\operatorname{xor}b\) 的取值,答案即为 \(\sum\limits_{i=0}^n f(i)\)
对 \(x\) 进行分类讨论:
-
\(x\bmod 2=1\):则 \(a,b\) 必为一奇一偶,故 \(\lfloor\frac{a}{2}\rfloor+\lfloor\frac{b}{2}\rfloor=\lfloor\frac{x}{2}\rfloor\),所以 \(x\) 的方案数和 \(\lfloor\frac{x}{2}\rfloor\) 完全一致,得 \(f(x)=f(\lfloor\frac{x}{2}\rfloor)\)
-
\(x\bmod 2=0\):则分两奇和两偶两种情况,用跟上面类似的方法,推得 \(f(x)=f(\frac{x}{2})+f(\frac{x}{2}-1)\)
综上:
然鹅此时还没完,因为 \(\sum\limits_{i=0}^n f(i)\) 这个式子不可能手动算,肯定还是要推。
令 \(s(i)=\sum\limits_{k=0}^i f(k)\),开始大力推式子:
然后你会发现,后面是有规律的,\(f(k-1)\) 往后的项系数均为 \(3\),则 \(s(2k)=s(k)+2s(k-1)\)
同理手推一遍,会发现 \(s(2k+1)=2s(k)+s(k-1)\)
综上:
直接 std::map 存储,记忆化搜索即可。
时间复杂度 \(\mathcal O(\log^2 n)\)
Code
// Problem: D - Xor Sum
// Contest: AtCoder - AtCoder Beginner Contest 050
// URL: https://atcoder.jp/contests/abc050/tasks/arc066_b
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
const int mod = 1e9 + 7;
std::map<i64,int> f;
i64 n;
int calc(i64 n) {
if(f[n])return f[n];
if(n <= 1)return f[n] = n + 1;
if(n & 1)return f[n] = (calc(n / 2) * 2 % mod + calc(n / 2 - 1)) % mod;
else return f[n] = (calc(n / 2) + calc(n / 2 - 1) * 2 % mod) % mod;
}
int main() {
scanf("%lld",&n);
printf("%d",calc(n));
return 0;
}
E - Addition and Subtraction Hard
给定一个含 \(n\) 个数,只含
+,-的中缀表达式,你可以在合法的前提下随意添加括号,令式子的计算结果最大化。\(1\le n\le 10^5\)
这题是怎么评上 *2700 的,不理解啊,这场的 D 不比这难得多?居然才 *2600,差评。
首先要发现在 + 后面放括号一点用也没有,只需要针对 -。
看样例,样例 3 最具有代表性,写下来,感觉不太够,再往里面随便放几个数:
8
1 - 20 + 2 - 13 + 14 - 5 - 18 + 7
我们强制往 \(-20\sim +7\) 这一段括起来,也就是对于 \(-20\) 的这个负号进行讨论。
此时的最佳方案应为 \(1-(20+2-(13+14)-5-(18+7))\)
发现一个结论:括号中第一个 - 后面所有的数都可以被转化为非负数。
构造很简单:抛开第一段连续的 +,把后面所有的连续的 + 都括起来即可(描述得不太清楚,参考我给的栗子)。
那么我们直接枚举 -,预处理出来第一段连续 + 的尾端,然后更新答案即可。
时间复杂度 \(\mathcal O(n)\)。
Code
// Problem: E - Addition and Subtraction Hard
// Contest: AtCoder - AtCoder Regular Contest 066
// URL: https://atcoder.jp/contests/arc066/tasks/arc066_c
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
const int maxn = 1e5 + 5;
char s[maxn];
int a[maxn],n,lst[maxn];
i64 pre[maxn],suf[maxn];
int main() {
scanf("%d %d",&n,&a[1]);
for(int i = 2;i <= n;++ i) {
scanf(" %c %d",&s[i],&a[i]);
}
pre[1] = a[1];
for(int i = 2;i <= n;++ i) {
if(s[i] == '-')pre[i] = pre[i - 1] - a[i];
else pre[i] = pre[i - 1] + a[i];
}
for(int i = n;i;-- i)
suf[i] = std::abs(a[i]) + suf[i + 1];
i64 ans = pre[n];
for(int i = n;i >= 2;-- i) {
if(s[i + 1] == '+')lst[i] = lst[i + 1];
else lst[i] = i;
}
for(int i = 2;i <= n;++ i) {
if(s[i] == '+')continue ;
ans = std::max(ans , pre[i - 1] - suf[i] + 2ll * suf[lst[i] + 1]);
}
printf("%lld",ans);
return 0;
}

ARC066
浙公网安备 33010602011771号