0%

2020CCPC-WWC-day5

A.Alternative Accounts

题目描述:

总共有 $ n $ 个账号参加 $ k(k \le 3) $ 场比赛,一个人可能有多个账号,但是一个人只能使用一个账号参加一场比赛。给出 $ k $ 场比赛的参与账号,问这些账号至少属于多少个人。

数据范围:
$ 1\le n \le 10^5, 1\le k \le 3, 1\le m \le n, 1\le x_i \le n $

题解:

当 $ k = 1 $ 时,有多少个账号就有多少个人

当 $ k = 2 $ 时,参加两场比赛的账号全是人, $ c $ 个,只参加第一场的为 $ a $ ,只参加第二场的为 $ b $ ,答案为 $ c + \max(a, b) $

当 $ k = 3 $ 时,参加三场比赛的全加上,参加 $ 1,2 $ 的可以和只参加 $ 3 $ 的绑定,只参加一场的可以匹配,得到答案

代码:

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
const int maxn = 1e5 + 10;
const int maxm = 1e5 + 10;
int n, k, a[maxn][5], m, ans;
int cnt[4][4];
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
read(n, k);
int ans = 0;
for (int i = 1, x; i <= k; i++)
{
read(x), a[i][0] = x;
for (int j = 1, y; j <= x; j++)
{
read(y), a[y][i] = 1;
}
}
if (k == 1)
{
printf("%d\n", a[k][0]);
}
else if (k == 2)
{
for (int i = 1; i <= n; i++)
{
if (a[i][1] && a[i][2])
ans++;
else if (a[i][1])
cnt[1][1]++;
else if (a[i][2])
cnt[2][2]++;
}
printf("%d\n", ans + max(cnt[1][1], cnt[2][2]));
}
else
{
for (int i = 1; i <= n; i++)
{
if (a[i][1] && a[i][2] && a[i][3])
ans++;
else if (a[i][1] && a[i][2])
cnt[1][2]++;
else if (a[i][1] && a[i][3])
cnt[1][3]++;
else if (a[i][2] && a[i][3])
cnt[2][3]++;
else if (a[i][1])
cnt[1][1]++;
else if (a[i][2])
cnt[2][2]++;
else if (a[i][3])
cnt[3][3]++;
}
cnt[3][3] -= min(cnt[3][3], cnt[1][2]);
cnt[2][2] -= min(cnt[2][2], cnt[1][3]);
cnt[1][1] -= min(cnt[1][1], cnt[2][3]);
ans += max(cnt[1][1], max(cnt[2][2], cnt[3][3]));
ans += cnt[1][2] + cnt[2][3] + cnt[1][3];
printf("%d\n", ans);
}
return 0;
}

E. Matching Problem

题目描述:

给出两个序列 $ a1, a_2, \cdots, a_m $ 和 $ b_1, b_2, b_3, b_4 $ ,两序列匹配的条件为:

  • 长度相等
  • $ x_i = x_j \Leftrightarrow y_i = y_j $

输出有多少个 $ a $ 的子序列和 $ b $ 匹配

数据范围:
$ 1\le n \le 300,1\le a_i \le n, 1\le b_i \le 4 $

题解:

数据范围比较小,这里直接枚举 $ a $ 中的三个数,然后判断第四个数有多少个。需要记录 $ a $ 中每个位置的数在后缀中出现的次数。

代码:

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
80
81
82
83
84
85
const int maxn = 1e3 + 10;
const int maxm = 1e5 + 10;
int t, n, m, k;
int a[maxn], b[maxn];
int cot[maxn][maxn];

bool check(int i, int j, int k)
{
int tmp[4] = {0, i, j, k};
for (int i = 1; i <= 3; i++)
{
for (int j = i + 1; j <= 3; j++)
{
if (!((a[tmp[i]] == a[tmp[j]] && b[i] == b[j]) || (a[tmp[i]] != a[tmp[j]] && b[i] != b[j])))
{
return false;
}
}
}
return true;
}
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
read(n);
for (int i = 1; i <= n; i++)
{
read(a[i]);
}
for (int i = 1; i <= 4; i++)
{
read(b[i]);
}
for (int i = n; i >= 1; i--)
{
for (int j = 1; j <= n; j++)
{
cot[i][j] = cot[i + 1][j];
}
cot[i][a[i]]++;
}
ll ans = 0;
int pp[4];
unordered_map<int, int> mp;
for (int i = 1; i <= n - 3; i++)
{
for (int j = i + 1; j <= n - 2; j++)
{
if ((a[i] == a[j] && b[1] == b[2]) || (a[i] != a[j] && b[1] != b[2]))
for (int k = j + 1; k <= n - 1; k++)
{
if (check(i, j, k))
{
int tmp = n - k;
pp[1] = i, pp[2] = j, pp[3] = k;
mp.clear();
for (int p = 1; p <= 3; p++)
{
if (b[p] == b[4])
{
tmp = cot[k + 1][a[pp[p]]];
break;
}
else
{
if (!mp.count(a[pp[p]]))
{
tmp -= cot[k + 1][a[pp[p]]];
mp[a[pp[p]]] = 1;
if (tmp < 0)
break;
}
}
}
ans += max(tmp, 0);
}
}
}
}
printf("%lld\n", ans);
return 0;
}

G. Cryptographically Secure PRNG

题目描述:

给出一个素数 $ p $ ,找出所有的 $ x $ 满足 $ inv(x) = min_{i = 2}^x inv(i) $ ,其中 $ mod = p $

数据范围:
$ 1\le T \le 500, 1\le p \le 10^9 $

题解:

当 $ p $ 非常大,所以直接算不太行。

因为逆元是相互的, $ x $ 的逆元是 $ inv(x) $ , $ inv(x) $ 的逆元是 $ x $ ,所以如果 $ inv(x) $ 是第一次出现,那么表示是出现了第一个 $ x $ 使得 $ inv(x) = x $ ,这时 $ (inv[x] = x) $ 也是一个答案,因此我们可以记录下 $ x $ 当第一次出现 $ 下标inv(x) = 下标x $ 时,将之前记录的答案再加入一遍,排序,输出。

代码:

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
const int maxn = 1e6 + 10;
const int maxm = 1e5 + 10;
int t, n, m, k;
ll p;
ll inv[maxn];
vector<pair<int, ll>> ans;
unordered_map<ll, ll> mp;
void init(int n, ll mod)
{
inv[0] = inv[1] = 1;
ll minx = INF_LL;
for (int i = 2; i <= n; i++)
{
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
minx = min(minx, inv[i]);
if (minx == inv[i])
{
if (!mp.count(inv[i]))
{
ans.push_back({i, inv[i]});
mp[i] = inv[i];
}
else
{
break;
}
}
}
}
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int tcase;
read(tcase);
while (tcase--)
{
read(p);
mp.clear();
ans.clear();
if (p <= 2)
{
printf("0\n");
continue;
}
init(1e5, p);
int t = ans.size();
for (int i = 0; i < t; i++)
{
if (ans[i].first == ans[i].second)
continue;
ans.push_back({ans[i].second, ans[i].first});
}
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for (auto x : ans)
{
printf("%d %lld\n", x.first, x.second);
}
}
return 0;
}

H. Geometry PTSD

题目描述:

找出单位圆上三点 $ A, B, C $ ,满足 $ \min(|AB|,|BC|, |AC|) \ge 1.7 $ 并且原点到平面 $ ABC $ 的距离小于等于 $ 1e^{-18} $ 大于 $ 0 $

输出三行,每行三个整数 $ x_i, y_i, z_i (-10^6 \leq x_i, y_i, z_i \leq 10^6, x^2 + y^2 + z^2 \neq 0) $ 代表一个点 $ (\frac{x}{\sqrt{x^2+y^2+z^2}},\frac{y}{\sqrt{x^2+y^2+z^2}},\frac{z}{\sqrt{x^2+y^2+z^2}}). $

数据范围:

题解:

发现需要找到这个平面,而且与原点距离非常近。可以先找一个过原点的等边三角形,然后放大倍数。 $ A(0, 1, 0), B(-\frac{\sqrt{3}}{2}, -\frac{1}{2}, 0), B(\frac{\sqrt{3}}{2}, -\frac{1}{2}, 0) $ 根据输出要求,直接每个点乘 $ 10^6 $ ,乘的越大误差越小,通过强制转 int ,可以保证不是恰好过原点。

代码:

1
2
3
4
5
import math
a = 1000000
print(0, a, 0)
print(int(math.sqrt(3)/2 * (-1)*a), int(-1/2 * a), 0)
print(int(math.sqrt(3)/2 * a), int(-1/2 *a), 0)

I. Practice for KD Tree

题目描述:

给出一个 $ n \times n $ 的矩阵,初始全为 $ 0 $ 。执行 $ m1 $ 次子矩阵加,然后执行 $ m2 $ 查询子矩阵最大值

数据范围:
$ 1\le n \le 2000, 1\le m_1 \le 5 \times 10^4 \\\\ 1\le m_2 \le 5 \times 10^5 $

题解:

先使用二维前缀和处理出子矩阵加之后的矩阵,然后使用二维线段树,没有修改,查询最大值。

二维线段树,首先以 $ x $ 为区间建立线段树,然后每个节点也都是一个线段树,即每个节点以 $ y $ 为区间建立线段树。

代码:

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const int maxn = 2e3 + 10;
const int maxm = 1e5 + 10;
int t, n, m1, m2, k;
ll a[maxn << 2][maxn << 2];
struct TwoSeg
{
ll MAX[maxn << 2][maxn << 2];
ll MIN[maxn << 2][maxn << 2];
ll minv, maxv;
void push_upx(int deep, int rt)
{
MAX[deep][rt] = max(MAX[deep << 1][rt], MAX[deep << 1 | 1][rt]);
// MIN[deep][rt] = min(MIN[deep << 1][rt], MIN[deep << 1 | 1][rt]);
}
void push_upy(int deep, int rt)
{
MAX[deep][rt] = max(MAX[deep][rt << 1], MAX[deep][rt << 1 | 1]);
// MIN[deep][rt] = min(MIN[deep][rt << 1], MIN[deep][rt << 1 | 1]);
}
void buildy(int ly, int ry, int deep, int rt, int flag)
{
//y轴范围ly,ry;deep,rt;标记flag
if (ly == ry)
{
if (flag != -1)
MAX[deep][rt] = a[flag][ly];
else
push_upx(deep, rt);
return;
}
int m = (ly + ry) >> 1;
buildy(ly, m, deep, rt << 1, flag);
buildy(m + 1, ry, deep, rt << 1 | 1, flag);
push_upy(deep, rt);
}
void buildx(int lx, int rx, int deep)
{
//建树x轴范围lx,rx;deep
if (lx == rx)
{
buildy(1, n, deep, 1, lx);
return;
}
int m = (lx + rx) >> 1;
buildx(lx, m, deep << 1);
buildx(m + 1, rx, deep << 1 | 1);
buildy(1, n, deep, 1, -1);
}
void updatey(int Y, int val, int ly, int ry, int deep, int rt, int flag)
{
//单点更新y坐标;更新值val;当前操作y的范围ly,ry;deep,rt;标记flag
if (ly == ry)
{
if (flag)
MAX[deep][rt] = MIN[deep][rt] = val;
else
push_upx(deep, rt);
return;
}
int m = (ly + ry) >> 1;
if (Y <= m)
updatey(Y, val, ly, m, deep, rt << 1, flag);
else
updatey(Y, val, m + 1, ry, deep, rt << 1 | 1, flag);
push_upy(deep, rt);
}
void updatex(int X, int Y, int val, int lx, int rx, int deep)
{
//单点更新范围x,y;更新值val;当前操作x的范围lx,rx;deep
if (lx == rx)
{
updatey(Y, val, 1, n, deep, 1, 1);
return;
}
int m = (lx + rx) >> 1;
if (X <= m)
updatex(X, Y, val, lx, m, deep << 1);
else
updatex(X, Y, val, m + 1, rx, deep << 1 | 1);
updatey(Y, val, 1, n, deep, 1, 0);
}
void queryy(int Yl, int Yr, int ly, int ry, int deep, int rt)
{
//询问区间y轴范围y1,y2;当前操作y的范围ly,ry;deep,rt
if (Yl <= ly && ry <= Yr)
{
// minv = min(MIN[deep][rt], minv);
maxv = max(MAX[deep][rt], maxv);
return;
}
int m = (ly + ry) >> 1;
if (Yl <= m)
queryy(Yl, Yr, ly, m, deep, rt << 1);
if (m < Yr)
queryy(Yl, Yr, m + 1, ry, deep, rt << 1 | 1);
}
void queryx(int Xl, int Xr, int Yl, int Yr, int lx, int rx, int rt)
{
//询问区间范围x1,x2,y1,y2;当前操作x的范围lx,rx;rt
if (Xl <= lx && rx <= Xr)
{
queryy(Yl, Yr, 1, n, rt, 1);
return;
}
int m = (lx + rx) >> 1;
if (Xl <= m)
queryx(Xl, Xr, Yl, Yr, lx, m, rt << 1);
if (m < Xr)
queryx(Xl, Xr, Yl, Yr, m + 1, rx, rt << 1 | 1);
}
} seg;

int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int x1, y1, x2, y2, k;
read(n, m1, m2);
for (int i = 1; i <= m1; i++)
{
read(x1, y1, x2, y2, k);
a[x1][y1] += k;
a[x1][y2 + 1] -= k;
a[x2 + 1][y1] -= k;
a[x2 + 1][y2 + 1] += k;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
seg.buildx(1, n, 1);
while (m2--)
{
read(x1, y1, x2, y2);
seg.maxv = -1;
seg.queryx(x1, x2, y1, y2, 1, n, 1);
printf("%lld\n", seg.maxv);
}
return 0;
}