NOIP 2023 模拟赛五 题解
A. [NOIP 2023 模拟赛五 By FXT A] 简单数学题
summarization
给出一个值域为 \([1,m]\) 的正整数序列 \(a_{1\sim n}\),序列中的数各不相同,求出使 \(a_i^2+a_j\) 为完全平方数的 \((i,j)\) 的对数。
solution
实际上就是求 \(x^2+y=z^2\quad(x,y,z\in\mathbb{N}^+)\) 的 \((x,y)\),其中 \(x,y\) 需要在序列中存在。
变形为 \(y=(z+x)(z-x)\),考虑 \(y\) 的所有成对因数。
枚举 \(y\),分解 \(y=a\times b\quad(a<b,a,b\in\mathbb{N}^+)\),根据 \(z+x=b,z-x=a\),求出 \(z,x\),最后统计答案。
由于枚举 \(y\) 太慢,考虑枚举因数 \(i\),再枚举 \(i\) 的倍数 \(ki\quad(k\ge2,ki\le m,k\in\mathbb{N}^+)\)
code
CI N = 1e6; int n, m, Tng[N + 5];
int main () {
RI i, j; for (Read (n, m), i = 1; i <= n; ++ i) {int x; Read (x); ++ Tng[x];}
ll ans = 0; for (i = 1; i <= N; ++ i) for (j = i; j <= N; j += i) {
int minn = min (i, j / i), maxx = max (i, j / i); if ((maxx - minn) % 2 == 0) ans += Tng[j] * Tng[(maxx - minn) / 2];
} ans /= 2; printf ("%lld\n", ans);
return 0;
}
B. [NOIP 2023 模拟赛五 By FXT B] 简单第三题
summarization
给出一个 \(1\) 到 \(N\) 的排列,你需要求出有多少个区间 \([L,R]\),满足这个区间的值是连续的。比如 \(2,3,1\) 是一个合法的区间,而 \(3,1\) 不是。(\(1\le N\le3\times10^5\))
solution1
考虑分治,将 \([L,R]\) 范围的答案拆为 \([L,mid],[mid+1,R]\quad(mid=\frac{L+R}2)\) 的答案和跨越 \(mid\) 的连续的区间的个数,所以只需 \(\mathcal{O}(n)\) 求出在 \([L,R]\) 范围内跨越 \(mid\) 的连续的区间的个数。
首先,对于一个区间 \([L,R]\) 是连续的,它的充要条件是 \(\text{最大值}-\text{最小值}=R-L\)
所以考虑预处理出两个数组 \(mx_i,mn_i\)。\(mx_i\) 表示在 \(1\le i\le mid\) 范围内,\([L,mid]\) 区间的后缀最大值和在 \(mid+1\le i\le R\) 范围内,\([mid+1,R]\) 区间的前缀最大值。\(mn_i\) 同理。
那么对于 \([L,R]\) 中一个区间 \([i,j]\quad(L\le i\le mid< j\le R)\),成为连续区间的条件变为:
考虑分类讨论 \(max(mx_i,mx_j)\) 和 \(min(mn_i,mn_j)\) 的取值情况。
一、\(max(mx_i,mx_j)=mx_i,min(mn_i,mn_j)=mn_i\)
发现 \(j=i+mx_i-mn_i\),枚举 \(i\) 判断合法情况即可
二、\(max(mx_i,mx_j)=mx_j,min(mn_i,mn_j)=mn_j\)
同一。
三、\(max(mx_i,mx_j)=mx_i,min(mn_i,mn_j)=mn_j\)
发现 \(mx_i+i=mn_j+j\)。先固定 \(i\),观察满足此情况时的不等条件:
由于 \(mx_j\) 单调不减,\(mn_j\) 单调不增,所以满足上式的 \(j\) 一定在一个区间 \([l,r)\quad(mid+1\le l<r\le R+1)\) 中,其中 \(l,r\) 满足 \(mn_i>mn_l,mn_i<mn_{l-1},mx_i>mx_{r-1},mx_i<mx_r\)。
当 \(i\) 左移一位后,\(mx_i\) 增加或不变,\(mn_i\) 减小或不变。发现 \(l,r\) 都需要向右移以满足新的不等条件。
于是将 \(i\) 从 \(mid\) 一位一位移到 \(L\),过程中维护 \(j\) 取值的区间,将在区间范围内的 \(j\) 的 \(mn_j+j\) 加入桶中,查询 \(mx_i+i\) 即可。桶的更新只需要在移动 \(l,r\) 时加入一个或删除一个即可。
四、 \(max(mx_i,mx_j)=mx_j,min(mn_i,mn_j)=mn_i\)
同三。
复杂度 \(\mathcal{O}(n)\)
分治算法总复杂度 \(\mathcal{O}(n\log n)\)
code1
CI N = 3e5; int n, a[N + 5], mx[N + 5], mn[N + 5], Tng[N * 3 + 5]; ll ans = 0;
void solve (int L, int R) {
RI i, j; if (L == R) return ; int mid = (L + R) >> 1; solve (L, mid); solve (mid + 1, R); mx[mid] = mn[mid] = a[mid]; mx[mid + 1] = mn[mid + 1] = a[mid + 1];
for (i = mid - 1; i >= L; -- i) mx[i] = max (mx[i + 1], a[i]), mn[i] = min (mn[i + 1], a[i]);
for (i = mid + 2; i <= R; ++ i) mx[i] = max (mx[i - 1], a[i]), mn[i] = min (mn[i - 1], a[i]);
for (i = L; i <= mid; ++ i) {j = i + mx[i] - mn[i]; if (j > mid && j <= R && mx[i] > mx[j] && mn[i] < mn[j]) ++ ans;}
for (j = mid + 1; j <= R; ++ j) {i = j - mx[j] + mn[j]; if (i <= mid && i >= L && mx[i] < mx[j] && mn[i] > mn[j]) ++ ans;}
int l = mid + 1, r = mid + 1; for (i = mid; i >= L; -- i) {
W (r <= R && mx[r] < mx[i]) ++ Tng[mn[r] + r + N], ++ r;
W (l < r && mn[l] > mn[i]) -- Tng[mn[l] + l + N], ++ l;
ans += Tng[mx[i] + i + N];
} W (l < r) -- Tng[mn[l] + l + N], ++ l;
l = mid, r = mid; for (j = mid + 1; j <= R; ++ j) {
W (l >= L && mx[l] < mx[j]) ++ Tng[mn[l] - l + N], -- l;
W (l < r && mn[r] > mn[j]) -- Tng[mn[r] - r + N], -- r;
ans += Tng[mx[j] - j + N];
} W (l < r) -- Tng[mn[r] - r + N], -- r;
}
int main () {
RI i, j; for (Read (n), i = 1; i <= n; ++ i) Read (a[i]); solve (1, n); printf ("%lld\n", ans + n);
return 0;
}