正睿 NOIP2023 20连 Day4 T3 题解

Description

称一个正整数对 (a,b)(a, b) 合法,当且仅当存在正整数 kkkk 组正整数对 (h1,w1),(h2,w2),,(hk,wk)(h_1, w_1), (h_2, w_2), \cdots, (h_k, w_k),使得:

  • i=1khiwi=a\sum_{i = 1} ^ k h_iw_i = a
  • i=1k2(hi+wi)=b\sum_{i = 1} ^ k 2(h_i + w_i) = b

给定 s,cs, c,求满足 a[1,s],b[1,c]a \in [1, s], b \in [1, c] 的正整数对中有多少是合法的。

对于 100%100\% 的数据,满足 1s2×105,1c4×105,2c1 \le s \le 2 \times 10^5, 1 \le c \le 4 \times 10^5, 2 \mid c

Solution

这里先把 cc 除以 22

不妨设 hiwih_i\leq w_i,易知 hish_i\leq \sqrt s

如果存在 hi=hjh_i=h_j,那么 (hi,wi),(hi,wj)(h_i,w_i),(h_i,w_j) 可以修改为 (hi,wi+wj1),(1,hi)(h_i,w_i+w_j-1),(1,h_i)

然后 11 的情况也可以消,(1,x),(1,y)(1,x),(1,y) 可以变为 (1,x+y1),(1,1)(1,x+y-1),(1,1),所以如果把 (1,1)(1,1) 删掉,对于每个 hh,只有唯一的一个 ww 与之对应。

如果把 (1,1)(1,1) 加上,会发现 2ab2a-b 的值不会变,所以可设 fif_i 表示当前 2ab=i2a-b=i 的最小的 bb

然后枚举 h,wh,w,可以得到转移方程:fifi(2hwhw)+h+wf_i\leftarrow f_{i-(2hw-h-w)}+h+w

把底下那玩意拆开,fifi+hw(2h1)+h+wf_i\leftarrow f_{i+h-w(2h-1)}+h+w

这样就只要枚举 hh,令 gig_i 表示 j+hw(2h1)=ij+h-w(2h-1)=i 的最小的 fj+wf_j+w,那么 gi=min{fi,gi(2h1)+1}g_i=\min\{f_i,g_{i-(2h-1)}+1\}

所以 fi=gi+h+hf_i=g_{i+h}+h

然后统计答案就枚举那个 2ab2a-b,不妨设为 iibb 要满足 2(i+b)2|(i+b)fib2nif_i\leq b\leq 2n-i

时间复杂度:O(ss)O(s\sqrt s)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 5e5 + 5;

int n, m;
int f[kMaxN], g[kMaxN];

void dickdreamer() {
std::cin >> n >> m;
m /= 2;
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; ++i)
f[i - 1] = i + 1;
for (int h = 2; h * h <= n; ++h) {
std::swap(f, g);
for (int i = 0; i <= 2 * n; ++i)
f[i] = g[i];
g[0] = 0;
for (int i = 0; i <= 2 * n + h; ++i) {
if (i > 2 * n) g[i] = 1e9;
if (i >= 2 * h - 1) g[i] = std::min(g[i], g[i - (2 * h - 1)] + 1);
}
for (int i = 0; i <= 2 * n; ++i)
f[i] = std::min(f[i], g[i + h] + h);
}
int64_t ans = 0;
for (int i = 0; i <= 2 * n; ++i) {
int l = f[i] + ((i + f[i]) & 1), r = std::min(m, 2 * n - i);
if (l <= r) ans += (r - l) / 2 + 1;
}
std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

正睿 NOIP2023 20连 Day4 T3 题解
https://sobaliuziao.github.io/2023/10/12/post/3f4ab0d1.html
作者
Egg_laying_master
发布于
2023年10月12日
许可协议