题解 - [LOJ 2286] 「WC2017」挑战

知名毒瘤卡常题。别去做,浪费时间

题目链接

原始题面

题目背景

你和同学们找了三道题目用来练习.

这次练习的目标是写出能在时间限制里通过尽量大规模数据的代码.

同学们纷纷写出了优秀的代码。现在,他们向你发起了挑战,他们对每个问题都设置了若干个测试数据,这是他们能通过的最大规模的测试数据。现在,他们想看一看你写的代码究竟能超过多少同学的代码,通过多大规模的测试数据.

本题分为 \(3\) 个任务,每个任务对应一道题和相应的若干个测试点,你需要对于每个任务,设计一个能通过尽量多测试点的程序.

题目描述

任务一

给定 \(n\)\(32\) 位无符号整数,将它们从小到大排序.

任务二

\(2n\) 个人在玩 「石头剪刀布」 游戏。他们排成两排,每排 \(n\) 个人。每个人在每一局游戏都使用固定策略,即对于第 \(i (i \in 1, 2)\) 排的第 \(j (0 \leq j < n)\) 个人,用一个整数 \(a_{ij}\)​​ 表示他的策略,其中 \(0\) 表示只出石头,\(1\) 表示只出剪刀,\(2\) 表示只出布.

现在有 \(q\) 个询问,每个询问给定三个整数 \(x,y,l(0\leq x,y<n,1\leq l\leq n-max(x,y))\), 问将第一排的第 \(x∼x+l-1\) 个人和第二排的第 \(y∼y+l-1\) 个人比赛之后,第一排有多少个人会赢.

上文中 「比赛」 的意思是,对于所有整数 \(i\) 满足 \(0\leq i<l\), 让第一排的第 \(x+i\) 个人和 第二排的第 \(y+i\) 个人进行 「石头剪刀布」 游戏.

任务三

我们称一个合法的括号串为:只由左括号和右括号构成,两种括号的数量相等,且任意一个前缀的左括号数量不少于右括号数量的串。现在给定一个由 (, )? 构成的串,问有多少种不同的方案,使得将每个 ? 都替换成一个括号之后,该串变成一 个合法的括号串。两种方案不同,当且仅当至少有一个位置的 ? 被替换成了不同的括号.

输入输出格式

输入格式

此题提供了模板程序。选手可以在此基础上编写自己的程序,模板程序详见下文数据范围与提示.

第一行一个整数 \(task\_id(1\leq task\_id\leq3)\), 表示任务编号。接下来是每个具体任务的输入内容.

在输入的同一行中,相邻的两个整数会被一个空格隔开.

对于任务一:一行,两个整数 \(n,s\) . 令 \(a_0=next\_integer(s),a_i=next\_integer(a_{i-1}),1\leq i<n\), 则 \(a_0,a_1,…,a_{n-1}\) 即为需要排序的 \(n\) 个整数.

对于任务二:第一行两个整数 \(n,q\) . 第二行一个仅包含 \(0, 1, 2\) 的长度为 \(n\) 的字符串,第 \(i\) 个字符所代表的整数表示第一排第 \(i\) 个人的策略(即 \(a_{1i}\)​​). 第三行格式同第二行,表示第二排各个人的策略.

对于任务三:第一行一个整数 \(n\), 表示给定的串的长度。第二行一个字符串,即为给定的串.

输出格式

对于任务 1: 令 \(b\) 为已经排好序的数组,调用 output_arr(b, n * 4) 即可.

对于任务 2: 将每个询问的答案依次存入 \(32\) 位无符号整数数组 \(b\) 中(即,存入 \(b_0,b_1,⋯,b_{q-1}\) 中), 然后调用 output_arr(b, q * 4) 即可.

对于任务 3: 输出一个整数,表示不同的方案数除以 \(2^{32}\)​​ 得到的余数.

输入输出样例

输入样例 #1

1
2
1
100000 2017012501

输出样例 #1

1
4275990336

输入样例 #2

1
2
3
4
5
6
7
8
9
10
2
6 6
200100
200211
5 1 3
2 0 1
2 0 3
2 0 2
2 3 4
0 1 3

输出样例 #2

1
3349208141

输入样例 #3

1
2
3
3
4
(???

输出样例 #3

1
2

输入样例 #4

1
2
3
3
4
)???

输出样例 #4

1
0

说明

数据范围与提示

任务编号分值测试点编号数据范围与约定
151\(n=100000\)
1192\(n=10^8\)
1113\(n=2\times10^8\)
274\(n=q=1000\)
2235\(n=q=300000\)
396\(n=1000\)
357\(n=120000\)
378\(n=225000\)
3149\(n=266666\)

模板程序

C++ 模板

Show code

C 模板

Show code

Pascal 模板

Show code

解题思路

任务一

基数排序,桶要开小些以便卡入 L1 Cache

任务二

直接 \(O(nq)\) 暴力,使用 popcount 指令和循环展开卡常

任务三

\(O(n^2)\) DP, 依旧是用循环展开卡常

代码参考

Show code

LibreOJ_2286view raw
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
* @Author: Tifa
* @Description: From <https://github.com/Tiphereth-A/CP-archives>
* !!! ATTENEION: All the context below is licensed under a
* GNU Affero General Public License, Version 3.
* See <https://www.gnu.org/licenses/agpl-3.0.txt>.
*/
#include <algorithm>
#include <iostream>
using namespace std;
using i32 = int32_t;
using i64 = int64_t;
using u32 = uint32_t;
#define _next(x) (((x ^= x << 13) ^= x >> 17) ^= x << 5)
#define _expanded_rep(len, repp, expressions) \
{ \
repp = len >> 3; \
while (repp--) { \
{ expressions } \
{ expressions } \
{ expressions } \
{ expressions } \
{ expressions } \
{ expressions } \
{ expressions } \
{ expressions } \
} \
switch (len & 7) { \
case 7: { \
expressions \
} \
case 6: { \
expressions \
} \
case 5: { \
expressions \
} \
case 4: { \
expressions \
} \
case 3: { \
expressions \
} \
case 2: { \
expressions \
} \
case 1: { \
expressions \
} \
} \
}
void output_arr(u32 *a, u32 &&size) {
u32 ret = size;
for (u32 x = 23333333, *_ = a + (size >> 2), *i = a; i < _; ++i) {
ret ^= *i + x;
_next(x);
}
cout << ret << '\n';
}
namespace task1 {
using data_type = u32 *;
using data_type2 = u32[256];
data_type2 cnt0, cnt8, cnt16, cnt24;
data_type a, b;
u32 n, seed;
void main() {
cin >> n >> seed;
a = (data_type)malloc(800000000);
b = (data_type)malloc(800000000);
for (u32 *_ = a + n, *i = a; i < _; ++i) {
*i = _next(seed);
++cnt0[seed & 255];
++cnt8[seed >> 8 & 255];
++cnt16[seed >> 16 & 255];
++cnt24[seed >> 24 & 255];
}
for (u32 i = 1; i < 256; ++i) {
cnt0[i] += cnt0[i - 1];
cnt8[i] += cnt8[i - 1];
cnt16[i] += cnt16[i - 1];
cnt24[i] += cnt24[i - 1];
}
for (u32 *i = a + n; --i >= a;) b[--cnt0[*i & 255]] = *i;
for (u32 *i = b + n; --i >= b;) a[--cnt8[*i >> 8 & 255]] = *i;
for (u32 *i = a + n; --i >= a;) b[--cnt16[*i >> 16 & 255]] = *i;
for (u32 *i = b + n; --i >= b;) a[--cnt24[*i >> 24 & 255]] = *i;
output_arr(a, n << 2);
free(a);
free(b);
}
} // namespace task1
namespace task2 {
#define _popcntll __builtin_popcountll
using data_type = i64[64][14063];
using str_type = char *;
using query_type = i32 *;
data_type f1, f2;
void set(data_type &f, u32 &&idx) {
if (idx < 64)
for (u32 i = 0; i <= idx; ++i) *f[i] |= 1ull << idx - i;
else
for (u32 i = 0, j; i < 64; ++i)
f[i][(idx - i) >> 6] |= 1ull << ((idx - i) & 63);
}
str_type s1, s2;
query_type q_x, q_y, q_len;
void main() {
u32 n, q;
cin >> n >> q;
s1 = (str_type)malloc(n + 1);
s2 = (str_type)malloc(n + 1);
cin >> s1 >> s2;
q_x = (query_type)malloc(q << 2);
q_y = (query_type)malloc(q << 2);
q_len = (query_type)malloc(q << 2);
for (u32 i = 0; i < q; ++i) cin >> q_x[i] >> q_y[i] >> q_len[i];
u32 *anss = new u32[q];
for (u32 idx = 0, i = 0; i < n; ++i, idx += 3) set(f1, idx + s1[i] - '0');
for (u32 idx = 0, i = 0; i < n; ++i, idx += 3)
set(f2, idx + s2[i] - '1' + 3 * (s2[i] == '0'));
i64 *_f1, *_f2;
for (u32 i = 0, len, repp, ans; i < q; ++i) {
q_x[i] *= 3;
q_y[i] *= 3;
q_len[i] *= 3;
len = q_len[i] >> 6;
repp = len >> 3;
ans = 0;
_f1 = task2::f1[q_x[i] & 63] + (q_x[i] >> 6);
_f2 = task2::f2[q_y[i] & 63] + (q_y[i] >> 6);
_expanded_rep(len, repp, ans += _popcntll(*_f1++ & *_f2++););
anss[i] = ans + _popcntll(*_f1 & *_f2 & (1ull << (q_len[i] & 63)) - 1);
}
output_arr(anss, q << 2);
free(s1);
free(s2);
free(q_x);
free(q_y);
free(q_len);
}
#undef _popcntll
} // namespace task2
namespace task3 {
using str_type = char *;
u32 _[266666 << 1 | 1], *f = _ + 266666;
u32 n;
str_type s;
u32 solve() {
*f = 1;
for (u32 i = 0, m, *f0, *f1, repp; i < n; ++i) switch (s[i]) {
case '(':
if (i & 1) *--f = 0;
break;
case ')': f += i & 1 ^ 1; break;
case '?':
m = (min(i, n - i) >> 1) + 1;
if (i & 1) {
*--f = 0;
++m;
}
f1 = (f0 = f) + 1;
_expanded_rep(m, repp, *f0 += *f1; f0 = f1++;);
}
return *f;
}
void main() {
cin >> n;
s = (str_type)malloc(n + 1);
cin >> s;
cout << solve() << '\n';
free(s);
}
} // namespace task3
void (* const _main[4])(void) = {
nullptr, task1::main, task2::main, task3::main};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
i32 task_id;
cin >> task_id;
return _main[task_id](), 0;
}