国庆 NOIP 模拟赛 Day 3
T1
40pts
枚举每个点作为中心,然后开两个指针往两边跳,同时计算答案。
100pts
只考虑右侧,定义i为中心,j为当前枚举的区域。
维护 \(i^2*s[i],s[i],i*s[i]\) 的前缀和。
T2
50 pts
读完题就可以发现,对答案产生贡献的条件是对应位上的 \(B[i]\) 的值要等于\(i\)。
所以我们可以设计一个用 \((B[i] == i)\) 更新答案的状态,想到答案一定是从形成长度较短的序列转移到较长的序列,所以可以设 \(f[i][j]\) 表示前 \(i\) 个位置形成长度为 \(j\) 的序列,答案最大是多少。
前 \(i\) 个形成长度为 \(j\) 的序列的答案一定能包含或大于前 \(i-1\) 个形成长度为 \(j\) 的序列的答案,所以 \(f[i][j] = f[i - 1][j]\) ,然后如果前 \(i-1\) 个形成了$ j-1$ 长度的序列,那 \(f[i][j] = f[i-1][j-1]+(B[i] == i)\) 。
# include <bits/stdc++.h>
using namespace std;
int f[1007][1007];
int a[1007];
int main () {
int n; cin >> n;
for (int i = 1; i <= n; ++ i) cin >> a[i];
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= i; ++ j) {
f[i][j] = max (f[i - 1][j - 1] + (a[i] == j), f[i - 1][j]);
}
}
int ans = 0;
for (int i = 1; i <= n; ++ i) ans = max (ans, f[n][i]);
cout << ans;
}
然后只会 50 pts……
然后不会了优化优化空间吧!
发现当前 i 的答案只需要 i-1 的答案,所以第一维可以省略,类似背包的转移,因为 \(f[i][j]\) 需要的是 \(f[i - 1][j-1]\) 的答案,所以要倒叙枚举,否则更新的时候是就不是上一维 \(i\) 的答案了。
# include <bits/stdc++.h>
using namespace std;
int f[1007], a[1007];
int main () {
int n; cin >> n;
for (int i = 1; i <= n; ++ i) cin >> a[i];
for (int i = 1; i <= n; ++ i) {
for (int j = i; j; -- j) {
f[j] = max (f[j - 1] + (a[i] == j), f[j]);
}
}
int ans = 0;
for (int i = 1; i <= n; ++ i) ans = max (ans, f[i]);
cout << ans;
}
T3
30 pts
因为相同的数不会产生影响,所以不用管,直接去重。
当某个区间最大值与最小值之差大于元素种类个数,这个区间就不符合题意,
否则是符合的。
所以可以维护一种不重集的东西,可以用 \(set\),根据平衡树原理插入删除是 \(logn\) 的,同时枚举区间,复杂度 \(O(n^2logn)\) 。
T4
30 pts
赛时没打出来,不懂哪错了……
大体思路就是,二进制枚举矩形每个点的颜色,然后每四个积木判断一下是否合法,然后累加答案。
复杂度是 \(O(2^{20})\) 带个 \(20\) 的常数。