# C

# 题目大意

如果一个数 nn,能够找到唯一一对 x,y(0<x<y)x, y~(0 < x < y),满足 x2+y2=nx^2+y^2=n 的话,那么就称 nn好数

现在,给你 nn,请问,1n1 \to n 有多少好数?请你输出他们

# 数据范围

  • 1n1071 \le n \le 10^7

# 题解

这题实际上考的是预处理,我们只需要定义一个 cnt ,然后预处理出所有数可能出现的次数即可了,复杂度是 O(N2)O( {\sqrt{N}}^2),可以接受

python
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
from math import sqrt

N = 10 ** 7 + 10

cnt = [0 for _ in range(N)]

for i in range(1, int(sqrt(N))):
j = i + 1
k = i * i
num = k + j * j

while num <= N - 10:
cnt[num] += 1
j += 1
num = k + j * j

n = int(input())

res = []
for i in range(n + 1):
if cnt[i] == 1:
res.append(i)

print(len(res))
for i in res: print(i, end = ' ')

# D

# 题目大意

给定 nn 个数,a1,,ana_1, \ldots, a_n,请问,满足以下条件的三元组 (i,j,k)(i, j, k) 有几个?

  • 1i,j,kn1 \le i, j, k \le n
  • ai:aj:ak=7:5:3a_i : a_j : a_k = 7 : 5 : 3
  • min(i,j,k)=jormax(i,j,k)=j\min(i, j, k) = j \quad or \quad \max(i, j, k) = j

# 数据范围

  • 1n3×1051 \le n \le 3 \times 10^5
  • 1ai1091 \le a_i \le 10^9

# 题解

显然,根据 min(i,j,k)=jormax(i,j,k)=j\min(i, j, k) = j \quad or \quad \max(i, j, k) = j 这个条件,我们可以得知,当确定了 jj 的位置时,i,ki, k 要么都在 jj 的左边,要么都在 jj 的右边,由此可以衍生出做法

我们用 map 记录左右两边的数出现的个数,然后扫描 j:1nj: 1 \to n,如果 aja_j55 的倍数,那么代表其可以当做中间的那个,我们只需要计算

cntl3×cntl7+cntr3×cntr7cnt_{l3} \times cnt_{l7} + cnt_{r3} \times cnt_{r7}

累加即可,其中, l3 = r3 = 3 * (a[j] / 5), l7 = r7 = 7 * (a[j] / 5)

python
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
l = dict()
r = dict()

n = int(input())
a = [0] + list(map(int, input().split()))

for i in range(1, n + 1):
r[a[i]] = r.get(a[i], 0) + 1

res = 0

for i in range(1, n + 1):
r[a[i]] -= 1

if a[i] % 5 == 0:
t = a[i] // 5
num7, num3 = t * 7, t * 3

cntL7 = l.get(num7, 0)
cntL3 = l.get(num3, 0)
cntR7 = r.get(num7, 0)
cntR3 = r.get(num3, 0)

# <-- 这里用乘法,和 C++ 一致
res += cntL7 * cntL3 + cntR7 * cntR3

l[a[i]] = l.get(a[i], 0) + 1

print(res)

# E

# 题目大意

nn 个人想放风筝,第 ii 个人站在 (ai,0)(a_i, 0),想把风筝放到 (bi,1)(b_i, 1)
问这 nn 个人,如果要求线不能交叉,能满足最多人放风筝的数量是多少?

# 数据范围

  • 1n2×1051 \le n \le 2 \times 10^5
  • 0ai,bi1090 \le a_i, b_i \le 10^9

# 题解

# 一些知识

为了写这道题,我们先介绍下最长上升子序列(LISLIS)和最长连续上升子序列(LCISLCIS

最长上升子序列:首先是 O(n2)O(n^2) 的做法,定义 dp[i] 为以 ii 结尾的最长上升子序列的长度,很容易可以得到状态转移方程

dpi=maxjt,a[t]<a[i]dpj+1dp_i = \max_{j \in t, a[\forall t] < a[i]} dp_j + 1

这样的是 O(n2)O(n^2) 的,那有没有 O(nlogn)O(n \log n) 的做法呢?有的兄弟有的
我们可以额外维护一个 dd 数组,did_i 表示,长度为 ii最长上升子序列的末尾元素的最小值
显然,did_i 是单调递增的,于是我们便可以用二分去找到位置

C++
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
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size(), res = 1;

vector<int> d(n + 1, INT_MAX); // d[i] 表示,长度为 i 的最长上升子序列,末尾的最小值
d[0] = -INT_MAX, d[1] = nums[0];

for (int i = 1; i < n; i++) {
int l = 0, r = i;

while (l < r) {
int mid = (l + r + 1) >> 1;

if (d[mid] >= nums[i]) r = mid - 1;
else l = mid;
}

res = max(res, l + 1);
d[l + 1] = min(d[l + 1], nums[i]);
}

return res;
}
};

最长连续上升子序列:这个很简单了,就是扫一遍 O(n)O(n) 解决

# 最终解答

其实这题就是,先按 aa 升序排,然后按 bb 降序排,排完后做一个 LISLIS 就结束了
为什么按 BB 降序排?
这是因为,如果按 BB 升序排,那同一个起点,比如 (1,2),(1,3),(1,4)(1, 2), (1, 3), (1, 4) 就会被算入 33,但实际上只算 11

python
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
class PII:
def __init__(self, a, b):
self.a = a
self.b = b

n = int(input())
seg = [PII(0, 0) for _ in range(n + 1)]

for i in range(1, n + 1):
a, b = list(map(int, input().split()))
seg[i] = PII(a, b)

seg[1 : n + 1] = sorted(seg[1 : n + 1], key = lambda x : (x.a, -x.b))
d = [10 ** 9 + 10 for _ in range(n + 1)]
d[1] = seg[1].b
d[0] = -1000000
res = 1

for i in range(2, n + 1):
l = 0
r = i - 1

while l < r:
mid = (l + r + 1) // 2

if d[mid] >= seg[i].b: r = mid - 1
else: l = mid

res = max(res, l + 1)
d[l + 1] = min(d[l + 1], seg[i].b)

print(res)

# F

# 题目大意

门松形序列:当且仅当一个序列 (a1,,an)(a_1, \ldots, a_n) 满足峰数 > 谷数

现在给你一个 (1,2,,N)(1, 2, \ldots, N) 的排列 PP,请问,PP 中子序列是门松形序列的个数?(不一定要求子序列连续)

# 数据范围

  • 1n3×1051 \le n \le 3 \times 10^5

# 题解

假设峰数量是 xx,谷数量是 yy
显然,对于任意一个序列,x-y \in \

一个超妙结论:只要子序列中存在 i<jk<li < j \le k < l,并且 ai<aj,ak>ala_i < a_j, \quad a_k > a_l,换句话说,如果序列两头是两个,即是满足题目要求的子序列

我们维护好两个数组

  1. bf[i] :在 ii 前比 aia_i 小的数的个数
  2. bh[i] :在 ii 后比 aia_i 小的数的个数

然后我们就可以计算了,对于 i<j=k<li < j = k < l 的情况,枚举 j(k)j(k) 即可,答案就是 bf[j] * bh[j]

接着考虑 i<j<k<li < j < k < l 的情况
我们枚举 kk,那么 ll 的数量是 bh[k] ,然后我们可以在 j=1j=k1j = 1 \to j = k - 1 中枚举 jj,那么 ii 的个数是 bf[j]j+1k1j + 1 \to k - 1 这一段的数字可以随便取,一共 kj1k-j-1 个,答案还要乘上 2kj12^{k - j - 1},也就是最终答案是

k=1nbhk×(j=1k1bfj×2kj1)\sum\limits_{k=1}^{n} bh_{k} \times (\sum\limits_{j = 1}^{k - 1} bf_j \times 2^{k-j-1})

但这样枚举的写法是 O(n2)O(n^2) 的,对式子进行变形

k=1nbhk×(j=1k1bfj×2j)×2k1\sum\limits_{k=1}^{n} bh_{k} \times (\sum\limits_{j = 1}^{k - 1} bf_j \times 2^{-j}) \times 2^{k-1}

可以发现,中间含 jj 的式子可以用前缀和维护

C++
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
int n;
vector<int> p, bf, bh;
vector<int> pw, ipw;

struct Tree {
vector<int> a;
int n;

Tree(int nn) {
a = vector<int>(nn + 1);
n = nn;
}

int lowbit(int x) {
return x & -x;
}

void add(int p, int x) {
while (p <= n) a[p] += x, p += lowbit(p);
}

int sum(int p) {
int res = 0;
while (p) res += a[p], p -= lowbit(p);
return res;
}
};

int qmi(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}

void solve() {
cin >> n;

p = vector<int>(n + 1);
bf = vector<int>(n + 1), bh = vector<int>(n + 1);
pw = vector<int>(n + 1), ipw = vector<int>(n + 1);

Tree bef(n), beh(n);

for (int i = 1; i <= n; i++) {
cin >> p[i];
bf[i] = bef.sum(p[i] - 1);
bef.add(p[i], 1);
}

for (int i = n; i >= 1; i--) {
bh[i] = beh.sum(p[i] - 1);
beh.add(p[i], 1);
}

pw[0] = 1;
for (int i = 1; i <= n; i++)
pw[i] = pw[i - 1] * 2 % MOD;

for (int i = 0; i <= n; i++)
ipw[i] = qmi(pw[i], MOD - 2);

int res = 0;

for (int i = 1; i <= n; i++)
res = (res + bf[i] * bh[i]) % MOD;

int pre = 0;

for (int i = 2; i < n; i++) {
res = (res + bh[i] * pre % MOD * pw[i - 1]) % MOD;
pre = (pre + bf[i] * ipw[i]) % MOD;
}

cout << res << '\n';
}

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

duoxichangan 微信支付

微信支付

duoxichangan 支付宝

支付宝