HNOI2014解题报告

HNOI2014解题报告

Author: Pengyihao


Day1 T1 画框


思路

这个题目我其实是没有思路的。

网上说要用一种高深的最小乘积生成树的算法,我就学了一下。

我们把每一种搭配方案中,\(A\) 的和记做 \(x\)\(B\) 的和记做 \(y\)

那么一种搭配方案就可以看做一个坐标 \((x, y)\)

因为 \(disharmony = x\cdot y\),所以我们可以把 \(disharmony\) 看作反比例函数的 \(k\)

因为反比例函数越靠近原点,\(k\) 越小。

所以我们要 \((x, y)\) 尽可能靠近原点。

找到 \(x\) 最小的方案 \(l\),和 \(y\) 最小的方案 \(r\),分别求出它们的坐标。

为什么大家都用KM算法,就我用费用流?好怕怕啊

然后我们就要找到在这两个方案的坐标的连线的下方,且三个方案形成的三角形面积最大的方案。

这个面积用叉积可以计算,最后发现形如 \(aA+bB\),其中 \(A, B\) 是题目中的那个东西。

于是我们可以再跑一遍费用流求出这个方案 \(mid\)

递归处理 \((l, mid)\)\((mid, r)\)

边界就是 \(l == mid || mid == r\)

这个据说递归次数期望是 \(\sqrt{\ln n}\),然后复杂度就对了。


代码

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
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (LL i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (LL i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
const LL MAXN = 80;
LL n, A[MAXN][MAXN], B[MAXN][MAXN], tmp[MAXN][MAXN];
namespace KM
{
using std::queue; queue<LL>q;
bool in_stack[MAXN << 1];
const LL ss = 0, tt = 159;
LL cnt, ret1, ret2, dis[MAXN << 1], pre[MAXN << 1];
LL head[MAXN << 1], data[MAXN * MAXN << 2], flow[MAXN * MAXN << 2];
LL wei1[MAXN * MAXN << 2], wei2[MAXN * MAXN << 2], nxt[MAXN * MAXN << 2], wei[MAXN * MAXN << 2];
void add(LL x, LL y, LL z, LL l, LL l1, LL l2)
{
nxt[cnt] = head[x]; data[cnt] = y; flow[cnt] = z;
wei[cnt] = l; wei1[cnt] = l1; wei2[cnt] = l2; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; flow[cnt] = 0;
wei[cnt] =-l; wei1[cnt] =-l1; wei2[cnt] =-l2; head[y] = cnt++;
}
bool bfs()
{
memset(pre, -1, sizeof pre);
memset(dis, 0x3f, sizeof dis);
dis[ss] = 0; q.push(ss); in_stack[ss] = true;
while (!q.empty()) {
LL now = q.front(); q.pop();
in_stack[now] = false;
for (LL i = head[now]; i != -1; i = nxt[i]) {
if (flow[i] && dis[data[i]] > dis[now] + wei[i]) {
dis[data[i]] = dis[now] + wei[i];
pre[data[i]] = i;
if (!in_stack[data[i]]) {
in_stack[data[i]] = true;
q.push(data[i]);
}
}
}
}
return pre[tt] != -1;
}
void dfs()
{
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) ret1 += wei1[pre[i]];
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) ret2 += wei2[pre[i]];
for (LL i = tt; pre[i] != -1; i = data[pre[i] ^ 1]) flow[pre[i]]--, flow[pre[i] ^ 1]++;
}
std::pair<LL, LL>main(LL argv[MAXN][MAXN])
{
ret1 = ret2 = 0; cnt = 0;
memset(head, -1, sizeof head);
FOR(i, 1, n) add(ss, i, 1, 0, 0, 0);
FOR(i, 1, n) FOR(j, 1, n) {
add(i, j + n, 1, argv[i][j], A[i][j], B[i][j]);
}
FOR(i, 1, n) add(i + n, tt, 1, 0, 0, 0);
while (bfs()) dfs();
return std::make_pair(ret1, ret2);
}
}
bool gongxian(std::pair<LL, LL>l, std::pair<LL, LL>mid, std::pair<LL, LL>r)
{
return (l.second - r.second) * (l.first - mid.first) == (l.second - mid.second) * (l.first - r.first);
}
LL get_ans(std::pair<LL, LL>l, std::pair<LL, LL>r)
{
if (l.first == r.first || l.second == r.second)
return Min(l.first * l.second, r.first * r.second);
FOR(i, 1, n) FOR(j, 1, n)
tmp[i][j] = -((r.second - l.second) * A[i][j] + (l.first - r.first) * B[i][j]);
std::pair<LL, LL>mid = KM::main(tmp);
if (gongxian(l, mid, r)) return Min(l.first * l.second, r.first * r.second);
return Min(get_ans(l, mid), get_ans(mid, r));
}
int main()
{
freopen("frame.in", "r", stdin);
freopen("frame.out", "w", stdout);
LL tcase; in(tcase);
while (tcase--) {
in(n);
FOR(i, 1, n) FOR(j, 1, n) in(A[i][j]);
FOR(i, 1, n) FOR(j, 1, n) in(B[i][j]);
std::pair<LL, LL>retA = KM::main(A), retB = KM::main(B);
printf("%lld\n", get_ans(retA, retB));
}
return 0;
}

Day1 T2 世界树


思路

这也是我没学过的算法——虚树。

首先用单调栈维护右链,把虚树构建出来;

然后对于虚树上的每条边,把它的分界点找出来,然后分段赋给管理端点的那两个点。

注意判断管理端点的两个点相同的情况。

这个题目的构建虚树需要用lca,我用了倍增。

这个题目找到分界点也需要用倍增。

没有使用数据结构,代码却比数据结构题还要长。


代码

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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <bits/stdc++.h>
typedef long long LL;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(); x = 0;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
const int MAXN = 300010;
bool is_chs[MAXN];
int n, q, num[MAXN], fa[MAXN], dfn[MAXN], sa[MAXN], depth[MAXN], sz[MAXN];
int INDEX, cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1], log_num[1000010];
int anc[MAXN][20], xsz[MAXN], val[MAXN];
void add(int x, int y)
{
nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;
}
void dfs(int now, int pa)
{
sz[now] = 1;
depth[now] = depth[pa] + 1;
fa[now] = pa; dfn[now] = ++INDEX;
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) {
dfs(data[i], now);
sz[now] += sz[data[i]];
}
}
}
void dfs_anc(int now, int pa)
{
anc[now][0] = pa;
for (int i = 1; anc[now][i - 1] != -1; i++) {
anc[now][i] = anc[anc[now][i - 1]][i - 1];
}
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) dfs_anc(data[i], now);
}
}
namespace get_lca
{
int eula[MAXN * 3], st[MAXN], ed[MAXN], IND, to[MAXN];
int minx[MAXN * 3][21];
void dfs2(int now)
{
eula[++eula[0]] = ++IND;
st[now] = eula[0]; to[IND] = now;
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != fa[now]) {
dfs2(data[i]);
eula[++eula[0]] = eula[st[now]];
}
}
ed[now] = eula[0];
}
int lca(int x, int y)
{
if (!x || !y) return 0;
if (st[x] > ed[y]) std::swap(x, y); x = st[x]; y = ed[y];
int delta = log_num[y - x + 1];
return to[Min(minx[x][delta], minx[y - (1 << delta) + 1][delta])];
}
void start()
{
FOR(i, 1, eula[0]) minx[i][0] = eula[i];
FOR(i, 1, 20) {
FOR(j, 1, eula[0]) {
if (j + (1 << i) - 1 > eula[0]) break;
minx[j][i] =
Min(minx[j][i - 1], minx[j + (1 << (i - 1))][i - 1]);
}
}
FOR(i, 1, 1000000) log_num[i] = log2(i);
}
}
int stack[MAXN], top, in_tree[MAXN];
int head_xu[MAXN], cnt_xu, nxt_xu[MAXN << 1], data_xu[MAXN << 1];
namespace build_tree
{
void add_xu(int x, int y)
{
nxt_xu[cnt_xu] = head_xu[x]; data_xu[cnt_xu] = y; head_xu[x] = cnt_xu++;
nxt_xu[cnt_xu] = head_xu[y]; data_xu[cnt_xu] = x; head_xu[y] = cnt_xu++;
}
bool cmp(int x, int y)
{
return dfn[x] < dfn[y];
}
void builds_tree()
{
cnt_xu = 0;
stack[top = 1] = 0;
in_tree[0] = 0;
FOR(i, 1, num[0]) {
is_chs[num[i]] = true;
in_tree[++in_tree[0]] = num[i];
}
in_tree[++in_tree[0]] = 0;
std::sort(in_tree + 1, in_tree + in_tree[0] + 1, cmp);
int now_in_tree = in_tree[0];
FOR(i, 2, now_in_tree) {
int now = in_tree[i];
int anc = get_lca::lca(stack[top], now);
if (anc == stack[top]) stack[++top] = now;
else {
while (true) {
int tp = stack[top], tp_l = stack[top - 1];
if (tp_l == anc) {
add_xu(tp_l, tp); top--; break;
}
else if (dfn[tp_l] > dfn[anc]) {
add_xu(tp_l, tp); top--;
}
else {
in_tree[++in_tree[0]] = anc;
add_xu(anc, tp); stack[top] = anc; break;
}
}
stack[++top] = now;
}
}
while (top != 1) {
add_xu(stack[top - 1], stack[top]); top--;
}
}
}
namespace find_father
{
int find(int x, int y)
{
for (int i = 19; i >= 0; i--) {
if (y & (1 << i)) {
x = anc[x][i];
}
}
return x;
}
}
namespace DP
{
int f[MAXN][2], g[MAXN][2], ret[MAXN];
void dp1(int now, int pa)
{
val[now] = 0;
xsz[now] = 1;
f[now][0] = is_chs[now] ? 0 : 0x3f3f3f3f;
f[now][1] = now;
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) {
dp1(data_xu[i], now);
xsz[now] += xsz[data_xu[i]];
int frm = depth[data_xu[i]] - depth[now];
if (f[now][0] > f[data_xu[i]][0] + frm ||
f[now][0] == f[data_xu[i]][0] + frm &&
f[now][1] > f[data_xu[i]][1])
{
f[now][1] = f[data_xu[i]][1];
f[now][0] = f[data_xu[i]][0] + frm;
}
}
}
}
void dp2(int now, int pa)
{
g[now][0] = f[now][0];
g[now][1] = f[now][1];
if (pa != -1) {
int frm = depth[now] - depth[pa];
if (g[now][0] > g[pa][0] + frm ||
g[now][0] == g[pa][0] + frm && g[now][1] > g[pa][1])
{
g[now][1] = g[pa][1]; g[now][0] = g[pa][0] + frm;
}
}
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) dp2(data_xu[i], now);
}
}
int getr(int x, int y, int z, bool t)
{
if (y - x + z < 0) return 0;
if (t) return (y - x + z) / 2;
else {
if ((y - x + z) % 2 == 0) return (y - x + z) / 2;
else return (y - x + z) / 2 + 1;
}
}
void dp3(int now, int pa)
{
if (!pa) {
ret[g[now][1]] += n - sz[now];
}
bool flag = false;
for (int i = head_xu[now]; i != -1; i = nxt_xu[i])
if (data_xu[i] != pa) {
flag = true;
int nnum = data_xu[i], all;
if (now) {
all = depth[nnum] - depth[now] - 1;
if (g[now][1] == g[nnum][1]) {
if (all) {
int pos = find_father::find(nnum, all);
ret[g[now][1]] += sz[pos] - sz[nnum];
val[now] -= sz[pos];
}
else val[now] -= sz[nnum];
}
else {
// ret[g[now][1]] += getr(g[now][0], g[nnum][0], all,
// g[now][1] > g[nnum][1]);
// ret[g[nnum][1]] += getr(g[nnum][0], g[now][0], all,
// g[nnum][1] > g[now][1]);
if (!all) {
val[now] -= sz[nnum];
}
else {
int Anc = getr(g[nnum][0], g[now][0], all,
g[nnum][1] > g[now][1]);
int pos = find_father::find(nnum, Anc);
ret[g[nnum][1]] += sz[pos] - sz[nnum];
int pos2 = find_father::find(nnum, all);
ret[g[now][1]] += sz[pos2] - sz[pos];
val[now] -= sz[pos2];
}
}
}
dp3(nnum, now);
}
}
void dp4(int now, int pa)
{
if (now) ret[g[now][1]] += sz[now] + val[now];
for (int i = head_xu[now]; i != -1; i = nxt_xu[i]) {
if (data_xu[i] != pa) dp4(data_xu[i], now);
}
}
void work()
{
build_tree::builds_tree();
FOR(i, 1, num[0]) ret[num[i]] = 0;
dp1(0, -1); dp2(0, -1); dp3(0, -1); dp4(0, -1);
FOR(i, 1, num[0]) printf("%d ", ret[num[i]]);
putchar(10);
}
}
int main()
{
freopen("worldtree.in", "r", stdin);
freopen("worldtree.out", "w", stdout);
memset(head, -1, sizeof head);
memset(head_xu, -1, sizeof head_xu);
in(n);
FOR(i, 1, n - 1) {
int x, y; in(x); in(y); add(x, y);
}
memset(anc, -1, sizeof anc);
dfs(1, 0); dfs_anc(1, 0);
get_lca::dfs2(1); get_lca::start();
FOR(i, 1, n) sa[dfn[i]] = i;
in(q);
FOR(i, 1, q) {
FOR(i, 1, num[0])
is_chs[num[i]] = false;
FOR(i, 1, in_tree[0])
head_xu[in_tree[i]] = -1;
in(num[0]);
FOR(j, 1, num[0]) in(num[j]);
DP::work();
}
return 0;
}

Day1 T3 米特运输


思路

根据每个点的权值,计算出根节点的权值。

然后用 \(n-\) 根节点权值的众数即可。

因为数据太大,所以需要取对数。


代码

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
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 500010;
long double rot[MAXN], val[MAXN];
int n, w[MAXN], tot[MAXN];
int cnt, head[MAXN], data[MAXN << 1], nxt[MAXN << 1];
void add(int x, int y)
{
nxt[cnt] = head[x]; data[cnt] = y; head[x] = cnt++;
nxt[cnt] = head[y]; data[cnt] = x; head[y] = cnt++;
}
void dfs1(int now, int pa)
{
for (int i = head[now]; i != -1; i = nxt[i])
if (data[i] != pa) {
tot[now]++;
dfs1(data[i], now);
}
}
void dfs2(int now, int pa)
{
for (int i = head[now]; i != -1; i = nxt[i]) {
if (data[i] != pa) {
val[data[i]] = log(tot[now]) + val[now];
dfs2(data[i], now);
}
}
}
const long double eps = 1e-6;
int main()
{
freopen("meat.in", "r", stdin);
freopen("meat.out", "w", stdout);
memset(head, -1, sizeof head);
in(n);
FOR(i, 1, n) in(w[i]);
FOR(i, 1, n - 1) {int u, v; in(u); in(v); add(u, v);}
dfs1(1, 0); dfs2(1, 0);
FOR(i, 1, n) rot[i] = val[i] + log(w[i]);
std::sort(rot + 1, rot + n + 1);
int ret = 0, line = 0;
FOR(i, 1, n) {
if (i == 1 || fabs(rot[i] - rot[i - 1]) > eps) {
chkmax(ret, line);
line = 1;
}
else line++;
}
chkmax(ret, line);
printf("%d\n", n - ret);
return 0;
}

Day2 T1 抄卡组


思路

首先,如果所有的字符串都有通配符,那么只需要两两比较前缀和后缀即可。

就是按每个字符串不包含通配符的最大前缀长度进行排序,比较一遍;

然后按每个字符串不包含通配符的最大后缀长度进行排序,比较一边。

如果所有的字符串都没有通配符,那么只需要比较hash值就可以了。

如果有些有通配符,有些没有通配符,将没有通配符的进行比较,看是否相同。

然后对于有通配符的,将通配符视作分隔符,即把原串分为一段一段的,分开进行匹配。

注意有通配符的字符串和没有通配符的字符串的首尾要对应。


代码

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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#include <bits/stdc++.h>
typedef long long LL;
typedef unsigned long long ULL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 2000010;
using std::string;
using std::cin;
using std::cout;
string str[MAXN];
int t, n, len[MAXN];
ULL hs[MAXN];
const ULL BASE = 31;
int prefix[MAXN], suffix[MAXN], numb[MAXN];
void gethash(int num)
{
hs[num] = 0;
FOR(i, 0, len[num] - 1) {
hs[num] = hs[num] * BASE + str[num][i];
}
}
bool isop[MAXN];
int nopera[MAXN];
ULL rhs[MAXN], po[MAXN];
ULL power(ULL x, ULL y)
{
if (!y) return 1;
return po[y];
}
void work()
{
string *a = str;
FOR(i, 1, nopera[0])
isop[nopera[i]] = false;
nopera[0] = 0;
FOR(i, 1, n) {
bool flag = true;
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') {
flag = false;
break;
}
}
if (flag) {
isop[i] = true;
nopera[++nopera[0]] = i;
}
}
FOR(i, 2, nopera[0]) {
if (hs[nopera[i]] != hs[nopera[i - 1]]) {
puts("N");
return;
}
}
int need_compare = nopera[1];
ULL TBASE = 1;
FOR(i, 1, len[need_compare]) {
rhs[i] = rhs[i - 1] + str[need_compare][i - 1] * TBASE;
TBASE = TBASE * BASE;
}
FOR(i, 1, n) if (!isop[i]) {
int lastpos = 0;
for (int j = 0, k; j <= len[i] - 1; j = k + 1) {
k = j;
while (k < len[i] && str[i][k] == '*') k++;
j = k;
while (k < len[i] && str[i][k] != '*') k++; k--;
if (str[i][k] == '*') break;
ULL nowhs = 0, TBASE = 1;
FOR(l, j, k) {
nowhs = nowhs + str[i][l] * TBASE;
TBASE = TBASE * BASE;
}
while (true) {
if (lastpos + (k - j + 1) > len[need_compare]) {
puts("N");
return;
}
if (k == len[i] - 1) {
lastpos = len[need_compare] - (k - j + 1);
}
ULL tmpnow = nowhs;
if (rhs[lastpos + (k - j + 1)] - rhs[lastpos] == tmpnow * power(31, lastpos)) {
lastpos += (k - j + 1);
break;
}
if (j != 0) {
lastpos++;
}
else {
puts("N");
return;
}
}
}
}
puts("Y");
}
bool cmp_prefix(int x, int y)
{
return prefix[x] < prefix[y];
}
bool cmp_suffix(int x, int y)
{
return suffix[x] < suffix[y];
}
int main()
{
freopen("hs.in", "r", stdin);
freopen("hs.out", "w", stdout);
po[0] = 1;
FOR(i, 1, 2000000) po[i] = po[i - 1] * BASE;
std::ios::sync_with_stdio(false);
cin >> t;
while (t--) {
cin >> n;
FOR(i, 1, n) cin >> str[i], len[i] = str[i].size();
bool isy = true, isn = true;
FOR(i, 1, n) {
gethash(i);
bool iss = false;
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') isn = false, iss = true;
}
if (!iss) isy = false;
}
if (isn) {
bool flag = false;
FOR(i, 2, n) if (hs[i] != hs[i - 1]) {
flag = true;
break;
}
if (!flag) puts("Y");
else puts("N");
continue;
}
else if (isy) {
FOR(i, 1, n) {
FOR(j, 0, len[i] - 1) {
if (str[i][j] == '*') {
prefix[i] = j; break;
}
}
DNF(j, len[i] - 1, 0) {
if (str[i][j] == '*') {
suffix[i] = len[i] - 1 - j; break;
}
}
}
bool flag = true;
FOR(i, 1, n) numb[i] = i;
std::sort(numb + 1, numb + n + 1, cmp_prefix);
FOR(i, 2, n) {
int last = numb[i - 1], now = numb[i];
FOR(j, 1, prefix[last]) {
if (str[last][j - 1] != str[now][j - 1]) {
flag = false; puts("N"); break;
}
}
if (!flag) break;
}
if (!flag) continue;
std::sort(numb + 1, numb + n + 1, cmp_suffix);
FOR(i, 2, n) {
int last = numb[i - 1], now = numb[i];
FOR(j, 1, suffix[last]) {
if (str[last][len[last] - j] != str[now][len[now] - j]) {
flag = false; puts("N"); break;
}
}
if (!flag) break;
}
if (flag) puts("Y");
continue;
}
else {
work();
}
}
return 0;
}

Day2 T2 道路堵塞


这是一道玄学题,需要依赖splay的复杂度的不确定性进行解题。

所以我跳过了它。


Day2 T3 江南乐


思路

首先用SG函数就可以做到 \(O(n^2)\) (枚举分开的情况,然后用朴素的统计就行了)

然后因为这题是分石子,我们可以发现分出来的石子很多都是一样的。

其实只可能分出两种石子,大小为 \(n mod i\) 的,和大小为 \(i-n mod i\) 的。

根据数量的奇偶性,最终会被消成不超过两个。

因为只关注奇偶性,所以对于 \(\lfloor n/i\rfloor\) 相等的多种分法,只需要计算最小的 \(i\)\(i+1\) 两种即可。

复杂度为 \(O(n\sqrt{n})\)


代码

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
#include <bits/stdc++.h>
typedef long long LL;
#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)
template <typename Tp> void in(Tp &x) {
char ch = getchar(), f = 1; x = 0;
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= f;
}
template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}
const int MAXN = 100010;
int t, f, sg[MAXN], can[MAXN];
int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
in(t); in(f);
FOR(i, 0, f - 1) sg[i] = 0;
FOR(i, f, 100000) {
for (int j = 2, k; j <= i; j = k + 1) {
k = i / (i / j);
int s1 = j, s2 = j + 1;
int a = ((i % s1) & 1) ? (i / s1 + 1) : 0;
int b = ((s1 - (i % s1)) & 1) ? (i / s1) : 0;
can[sg[a] ^ sg[b]] = i;
a = ((i % s2) & 1) ? (i / s2 + 1) : 0;
b = ((s2 - (i % s2)) & 1) ? (i / s2) : 0;
can[sg[a] ^ sg[b]] = i;
}
FOR(j, 0, 100000)
if (can[j] != i) {
sg[i] = j;
break;
}
}
FOR(i, 1, t) {
int n, ret = 0; in(n);
FOR(j, 1, n) {
int x; in(x); ret ^= sg[x];
}
if (ret) printf("1");
else printf("0");
if (i != t) printf(" ");
}
return 0;
}