Loading

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)\)

综上:

\[f(x)= \begin{cases} f(\lfloor\frac{x}{2} \rfloor),& \text{if } x \text{ is odd} \\ f(\frac{x}{2})+f(\frac{x}{2}-1),& \text{otherwise} \end{cases} \]

然鹅此时还没完,因为 \(\sum\limits_{i=0}^n f(i)\) 这个式子不可能手动算,肯定还是要推。

\(s(i)=\sum\limits_{k=0}^i f(k)\),开始大力推式子:

\[\begin{aligned} s(2k) & = f(k)+f(k-1)+s(2k-1)\\ & = f(k)+2f(k-1)+s(2k-2)\\ & = f(k)+3f(k-1)+f(k-2)+s(2k-3)\\ & = f(k)+3f(k-1)+2f(k-2)+s(2k-4)\\ & = f(k)+3f(k-1)+3f(k-2)+f(k-3)+\dots\\ & \vdots \end{aligned} \]

然后你会发现,后面是有规律的,\(f(k-1)\) 往后的项系数均为 \(3\),则 \(s(2k)=s(k)+2s(k-1)\)

同理手推一遍,会发现 \(s(2k+1)=2s(k)+s(k-1)\)

综上:

\[s(i)=\begin{cases} s(\lfloor\frac{i}{2}\rfloor)+s(\lfloor\frac{i}{2}\rfloor-1),& \text{if } i \text{ is even}\\ 2s(\lfloor\frac{i}{2}\rfloor)+s(\lfloor\frac{i}{2}\rfloor-1),& \text{otherwise} \end{cases} \]

直接 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;
}
posted @ 2022-11-03 17:08  Skylakes  阅读(39)  评论(0)    收藏  举报