『模拟赛』多校A层冲刺NOIP2024模拟赛16
Rank
依托,给我烂完了(
A. 四舍五入
唐题,赛时被硬控 3h。
发现枚举 \(i\) 是一个很没前途的选择,分成三段后仍然需要 \(\mathcal{O(n)}\) 去跑 \(\left[1,\lfloor{\frac{i}{2}}\rfloor\right]\) 这一段,复杂度仍是 \(\mathcal{O(n^2)}\) 的,只有 30pts。
正难则反,我们换个角度考虑枚举 \(j\),对于一个 \(j\) 而言,其有贡献的 \(i\) 的区间是固定的,我们可以记录这些区间,用差分的思路做,得出每个 \(i\) 的答案,但这样算会有调和级数 \(n\ln n\) 左右段区间,空间就炸了,因此直接记录在每个 \(i\) 上对后续产生的新贡献即可,这样空间就是线性的,时间复杂度是 \(\mathcal{O(n\ln n)}\)。注意区间的右端点不特殊处理的话最大会达到 \(2n\),空间要开大一倍。
ps:发现用逗号会比用分号慢 100ms 左右,望周知。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar(); lx x = 0, f = 1;
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 2e6 + 5;
const int mod = 1e9 + 7;
int n, tot;
int a[N << 1];
namespace Wisadel
{
short main()
{
freopen("count.in", "r", stdin), freopen("count.out", "w", stdout);
n = qr;
fo(i, 1, n) for(int l = 0; l <= n; l += i)
{
a[l]++;
a[l + i / 2 + (i & 1)]--;
}
int now = 1, res = a[0];
fo(i, 1, n)
{
res += a[i];
printf("%d ", res);
}
return Ratio;
}
}
signed main(){return Wisadel::main();}
B. 填算符
人类智慧思维题。
发现这 \(k\) 个 & 放到前 \(k\) 个空中一定能保证权值最大,因此 \(\mathcal{O(n^2)}\) 的思路很好出,我们双指针每次考虑 \(i\) 是否能替换 \(j\) 上的 &,判断标准直接 \(\mathcal{O(n)}\) 跑一遍算出结果比较即可。
发现我们做了巨大多次无用计算,考虑用 st 表优化。依旧是倒序考虑,考虑当前位 \(i\) 能否放置 &,只需要用 st 表求出 \(\left[1,i\right]\) 中前 \(j-1\) 个为 & 后面为 | 的值再 & 上当前值 \(a_{i+1}\),判断其能否满足当前的需求,需求初始为最大权值。若满足则直接将其入栈记录答案,否则该位上符号为 |,那么 \(a_{i+1}\) 一定对答案有贡献,我们的需求可以进一步降低为最大权值中 \(a_{i+1}\) 无法满足的,即二进制下一些最大权值为 1 而 \(a_{i+1}\) 不为 1 的数位的按位或和。
感性理解下,就是我们现在需要满足一些条件,其中一些条件已经确定可以满足,那么前面只需满足其他的条件即可。
理解了这道题就做完了,复杂度主要是 st 表预处理的 \(\mathcal{O(n\log n)}\),双指针下判断可以 \(\mathcal{O(n)}\) 做。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar(); lx x = 0, f = 1;
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
return x * f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
#define pii pair<int, int>
#define P_B(x) push_back(x)
#define M_P(x, y) make_pair(x, y)
const int Ratio = 0;
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int n, k;
int lg[N];
ll a[N], sta[30][N], sto[30][N];
bool yz[N];
stack<int> ans;
namespace Wisadel
{
inline ll Wgta(int l, int r)
{
if(l > r) return 0;
int d = lg[r - l + 1];
return sta[d][l] & sta[d][r - (1 << d) + 1];
}
inline ll Wgto(int l, int r)
{
if(l > r) return 0;
int d = lg[r - l + 1];
return sto[d][l] | sto[d][r - (1 << d) + 1];
}
inline ll W(int l, int mi, int r)
{
if(mi + 2 > r) return 0;
return Wgta(1, mi + 1) | Wgto(mi + 2, r);
}
short main()
{
freopen("bitop.in", "r", stdin), freopen("bitop.out", "w", stdout);
n = qr, k = qr;
fo(i, 1, n) sta[0][i] = sto[0][i] = a[i] = qr;
fo(i, 2, n) lg[i] = lg[i >> 1] + 1;
fo(i, 1, lg[n]) fo(j, 1, n - (1 << i) + 1)
sta[i][j] = sta[i - 1][j] & sta[i - 1][j + (1 << (i - 1))],
sto[i][j] = sto[i - 1][j] | sto[i - 1][j + (1 << (i - 1))];
ll tot = W(1, k, n);
int j = k;
fu(i, n - 1, 1)
{
if(i <= j || !j) break;
if(((W(1, j - 1, i) & a[i + 1]) & tot) == tot)
ans.push(i), j--;
else tot ^= (a[i + 1] & tot);
}
while(j) ans.push(j--);
while(ans.size()) printf("%d ", ans.top()), ans.pop();
puts("");
return Ratio;
}
}
signed main(){return Wisadel::main();}
C. 道路修建
比较困难的数据结构题。硬暴力有 10pts。
D. 逆序图
神秘题,暴力都不会打。
末
好烂的一场,开场切不出 T1 会无形带来巨大的压力,然后整场思维感觉都被冻住了,T1 T2 感觉都不算很难,但都只打了最低一档暴力分。
下午体育课,挥洒汗水后思维确实活跃了不少,鉴定为早上没跑操 + 没早读导致的(?)
出题人说上 100 没有 CSP-S 难(
完结撒花~
想什么呢