noip2020 模拟赛

记录考试总结

2020.09.05

比赛入口


比赛时一开始把T1做了,用了个错算法,但是我当时不知道。随后想了一会儿做了T2。最后半个小时一对拍就发现T1用了个错的dp方法,于是赶紧写了个60pts暴力交上去。
然而,T2由于把题目理解错了,只计算了 i=1npi\sum_{i=1}^{n}p_i ,导致100pts$\to$20pts。

最终得分:80


Solution:

  1. 无线网路发射器选址

经典的求最大矩形问题:悬线法。


hi,jh_{i, j} 为以 (i,j)(i,j) 为起点,向上的最长空线段(即悬线)长度。
li,jl_{i,j}(i,j)(i,j) 向左最多拓展到的点
ri,jr_{i,j}(i,j)(i,j) 向右最多拓展到的点
si,js_{i,j} 为悬线最多向左移动到的列编号
ti,jt_{i,j} 为悬线最多向右移动到的列编号

则有

ans=max(hi,j×(ti,jsi,j+1))(1in,1jm)ans=max \left(h_{i,j}\times(t_{i,j}-s_{i,j}+1)\right)\quad(1\le i\le n,1\le j\le m)

递推式

si,j=max(si1,j,li,j)s_{i,j}=max(s_{i-1,j},l_{i,j})

ti,j=min(ti1,j,ri,j)t_{i,j}=min(t_{i-1,j},r_{i,j})

code

  1. 联合权值

树形dp

求联合联通块(即下图左边图形)的数量,要求每一条直线在树上都是一条链。

考虑分开考虑

形式 意义
cnticnt_i 节点 ii 的儿子数量
soni,json_{i,j} 节点 ii 的第j个儿子,简写为 sonjson_j
subtreeisubtree_i 节点 ii 的子树
fif_i ii 为深度较小的端点的链的数量,即上图编号1
gig_i ii 为顶点的两条链的方案数,即上图编号2
hi,jh_{i,j} ii 为顶点的两条链,且其中一条链经过 ii 的第 jj 个儿子的方案数。故有 gi=(j=1cntihi,j)/2g_i=(\sum_{j=1}^{cnt_i}h_{i,j}) / 2
wiw_i 节点 ii 的子树内所有点的 gg 值和,即 jsubtreeigj\sum_{j\in subtree_i}g_j
pip_i 以节点 ii 为“根”的上图编号3的图形的数量

可以发现,答案为:

ans=i=1njsubtreeipjans=\sum_{i=1}^{n}\sum_{j\in subtree_i}p_j

依次递推,求得答案。

fi=j=1cntifsonj+1f_i=\sum_{j=1}^{cnt_i}f_{son_{j}}+1

hi,j=fsonj×((k=1cntifsonk)fsonj)h_{i,j}=f_{son_{j}}\times \left( (\sum_{k=1}^{cnt_i}f_{son_k})-f_{son_j}\right)

gi=j=1cnti hi,j2g_i=\frac{\sum_{j=1}^{cnt_i}\ h_{i,j}}{2}

wi=jsubtreeigjw_i=\sum_{j\in subtree_i}g_j

pi=j=1cnti((gihi,j)×wsonj)p_i=\sum_{j=1}^{cnt_i}\left((g_i-h_{i,j})\times w_{son_j}\right)

复杂度 O(n2)O(n^2)

code


2020.09.06

入口 contest_show.php?cid=682

这一天的比赛还算顺利,T1、T3写出来了,T2写了个 O(n2)O(n^2) 暴力水了50pts。


Solution

T1

题意:有 nn 个相同的弹珠, kk 个相同的盒子。现在随机的将每个弹珠扔进盒子中,使得最终每个盒子非空,求出一共有多少种不同方案,对 998244353998244353 取模。

不难想到一个略暴力的dp方法:设 f(i,j)f(i,j) 表示将 jj 个弹珠放到 ii 个盒子(每个盒子可以放的数量 xx 满足 0xj0\le x\le j ,且 x1x2 ... xi1xix_1\le x_2\le\ ...\ \le x_{i-1}\le x_i)的方案数。

这里我们设第一个盒子里面放了 kk 个弹珠,则剩下的 i1i-1 个盒子里面每个盒子至少要放 kk 个弹珠才能满足 x1x2 ... xi1xix_1\le x_2\le\ ...\ \le x_{i-1}\le x_i。此时剩下 i1i-1 个盒子和 jkij-ki 个弹珠,方案数为 f(i1,jki)f(i-1, j-ki)
因此有状态转移方程:

f(i,j)=k=0 j/i f(i1,jki)f(i,j)=\sum_{k=0}^{\left\lfloor\ j/i\ \right\rfloor}f(i-1,j-ki)

特别地,f0,0=1f_{0,0}=1 。答案为 f(k,nk)f(k,n-k)

这个算法的复杂度为 O(n3)O(n^3)

考虑优化:
观察一下状态转移的来源

f(2,7)f(2,7) 为例:

1
2
3
4
f(1, 7) -> f(2, 7)
f(1, 5) -> f(2, 7)
f(1, 3) -> f(2, 7)
f(1, 1) -> f(2, 7)

可以发现,第二维 jkij-kiii 取模是一个固定的值,且等于 jmodij\bmod i 。不妨令 jki=pj-ki=p ,方便表示。在上面的例子中,pj(modi)p\equiv j\pmod{i} 。且取值范围 jmodikjj\bmod i\le k\le j ,所以 kk 构成一个序列: jmodij\bmod i(jmodi)+i(j\bmod i)+i(jmodi)+2i(j\bmod i)+2i ,… ,(jmodi)+jii(j\bmod i)+\lfloor\frac{j}{i}\rfloor i

g(p,k)g(p,k) 表示第一项为 pp 的序列的第 0...k0...k 个元素之和。
f(i,j)=g(jmodi,ji)f(i,j)=g(j\bmod i,\lfloor\frac{j}{i}\rfloor)

考虑 f(i,j)f(i,j)gg 数组的影响。

p=jmod(i+1)p=j\bmod (i+1)q=ji+1q=\left\lfloor\dfrac{j}{i+1}\right\rfloor

g(p,q)={f(i,j)(q=0)g(p,q1)+f(i,j)(q1)g(p,q)=\begin{cases}f(i,j)&\quad(q=0)\\g(p,q-1)+f(i,j)&\quad(q\ge 1)\end{cases}
实际编写过程中要使用滚动数组,防止 gg 数组被覆盖。

实际上把 f(i,j)f(i,j) 代入到上式中可以化简递推式,去掉一个数组 可是我懒得搞

code


T2

给出一棵树,有边权,求该树中某路径长度kk 使得 k>=Sk>=Sk<=Ek<=E

一道很经典的点分治

可以每次以现在森林中每棵树的重心心作为根计算答案,一条路径的长度只有经过节点 rootroot 和不经过 rootroot 两种情况,其中不经过 rootroot 的方案可通过递归 rootroot 的子树来求答案。

那么每次设 rootroot 的子树每个节点的 d(v)d(v) 表示节点 vvrootroot 的距离。

易知经过 rootroot 的路径一定为 d(u)d(u)d(v)d(v) 组成,那么考虑给该子树的所有节点编号按 d(v)d(v) 进行排序,每次选取两个节点求他们的 d(u)+d(v)d(u)+d(v) 值即为路径答案。

将子树存储在数组 aa 中,大小为 cntcnt
l=1l=1r=cntr = cnt

如果当前d(al)+d(ar)>Ed(a_l)+d(a_r)>E 那么说明 rr 的下标取太大了,将 rr 赋值为 r1r-1
如果当前d(al)+d(ar)<Sd(a_l)+d(a_r)<S 那么说明左端点为 ll 的情况已经考虑完了,将 ll 赋值为 l+1l+1

倘若找到符合条件的 llrr 满足 Sd(al)+d(ar)ES\le d(a_l)+d(a_r)\le E,仍需考虑一个问题:如果 ala_lara_r 在同一棵子树内,那么它们之间的路径不经过节点 rootroot ,考虑如何解决这个问题:

如果 Sd(al)+d(ar1)ES\le d(a_l)+d(a_{r-1}) \le E,那么考虑 rr 自减可能会找到一种情况使得 ala_lara_r 不在同一棵子树内,就会找到一组解

否则 ala_l 肯定找不到一组解使得Sd(al)+d(ar)ES\le d(a_l)+d(a_{r}) \le Eala_l 就一定无法对答案造成影响,将 ll 赋值为 l+1l+1 继续找解。

复杂度O(nlog2n)O(nlog^2n)
code


T3

要求在 [1,n][1,n] 范围内有多少无序数对满足gcd(a,b)=a xor bgcd(a,b)=a \ xor\ b

  1. 引理1

gcd(a,b)=a xor bgcd(a,b)=a\ xor\ b

则 $gcd(a,b)\ xor\ gcd(a,b)\ xor\ a=a\ xor\ b\ xor\ a\ xor\ gcd(a,b) $

那么 a=gcd(a,b) xor ba=gcd(a,b)\ xor\ b

  1. 引理2

易知 gcd(a,b)×k=abgcd(a,b) \times k=a-b

因此得到 gcd(a,b)<=abgcd(a,b) <= a-b

  1. 引理3

观察 aba-ba xor ba\ xor\ b

对于两个数的第 kk
| -----------: | -----------: | -----------: | -----------: | -----------: |
| num1 | 0 | 0 | 1 | 1 |
| num2 | 0 | 1 | 0 | 1 |
| xor | 0 | 1 | 1 | 0 |
| 减法 | 0 | -1 | 1 | 0 |

对比表格会发现

ab<=a xor ba-b<=a\ xor\ b

根据已求证的引理2和引理3得到

gcd(a,b)<=ab<=a xor bgcd(a,b)<=a-b<=a\ xor\ b

又由题目条件:gcd(a,b)=a xor bgcd(a,b)=a\ xor\ b
不难得到gcd(a,b)=ab=a xor bgcd(a,b)=a-b=a\ xor\ b
那么只需枚举 aba-b 的值,并且 gcd(a,b)=abgcd(a,b)=a-b ,那么两个数 aabb 可表示为:a=k×i,b=(k+1)×ia=k \times i,b=(k+1) \times i

每次得到两个数 aabb 时,还需检验第三个条件 ab=a xor ba-b=a\ xor\ b 即可
code


2020.09.19

contest_show.php?cid=691


Review

在手推样例之前,不要试图以任何方式写代码。

T1是一个比较有意思的题目,但是我一开始把 “小 T\small\mathtt{T} 的乐谱与乐谱 P\small\mathtt{P}最长公共子序列长度为 E\small\mathtt{E}” 理解成了 “小 T\small\mathtt{T} 的乐谱与乐谱 P\small\mathtt{P}最长公共连续子序列长度为 E\small\mathtt{E}”,而且只看了样例的最后一行就开始敲代码。
一个小时之后,等我把 hash\small\mathtt{hash} 调完,输入样例的那一刻,结果发现样例第一组数据不是连续的!人都傻了。
幸好这个 dp\small\mathtt{dp} 不是很难,冷静之后,搞了几十分钟后还是写了出来,但是就失去了想剩下两题正解的时间。

T2是所谓莫队算法,很久以前学的,好像忘了,需要在接下来的一个星期之内重新学一下。

T3也是一个 dp\small\mathtt{dp},但是需要一些数学处理。


Solution

1.Piano

这里记录两种方法

先是题解做法:

“保证小 T 的乐谱中每个音符都不会重复出现。”
注意到这个条件,我们可以把 LCS\small\mathtt{LCS} 问题转换成 LIS\small\mathtt{LIS}(最长上升子序列)问题。
数据是随机的,所以先将乐谱 A 中的字符串哈希,转成数值,
(总共只有 5×7×1065\times7\times10^6 种哈希值,所以哈希值之间没有冲突。)
然后通过哈希值判断,将乐谱 B 中无效的音符去除。
CiC_i 为标号序列, 若 AiA_i = BjB_j ,那么 CjC_j = ii
接下来对序列 CCLIS\small\mathtt{LIS} 就可以啦~!
时间复杂度: O(S+RL(logL+logS))O\left( S + RL(logL+logS) \right)

然后是我的考场做法:

f(i,j)f(i, j) 表示 A1iA_{1\to i}B1jB_{1\to j}LCS\small\mathtt{LCS} 且满足 AiA_i = BjB_jpos(x)pos(x) 满足 Bi=Apos(i)B_i=A_{pos(i)}

则有状态转移方程

f(pos(p),p)=max((maxi,j=1i<pos(p),j<pf(i,j))+1,maxi,j=1ipos(p),j<pf(i,j))f(pos(p),p)=\max\left(\left(\max_{i,j=1}^{i<pos(p),j<p}f(i,j) \right)+ 1, \max_{i,j=1}^{i\le pos(p),j<p}f(i,j)\right)

f(pos(p),p)=mf(pos(p),p)=m

理解:f(i,j)f(i,j) 满足 AiA_iBjB_j 匹配,又因为 Apos(p)A_{pos(p)}BpB_{p} 匹配,所以 f(pos(p),p)=f(i,j)+1f(pos(p),p)=f(i,j)+1
为什么 iipos(p)pos(p) 不能取等号呢?由于可能有多个 p1p_1p2p_2p1<p2p_1 < p_2)对应同一个 pos(p)pos(p),那这时 f(pos(p),p2)f(pos(p),p1)+1f(pos(p), p_2)\not=f(pos(p), p_1)+1 ,因为Apos(p)A_{pos(p)} 不能使用两次。但是 f(pos(p),p2)f(pos(p),p_2) 可以等于 f(pos(p),p1)f(pos(p),p1) ,相当于 AposA_{pos} 只与 Bp1B_{p_1} 匹配,不与 Bp2B_{p_2} 匹配。

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
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1e5 + 10;

struct Pos_val_pair {
int val, pos;
const bool operator < (const Pos_val_pair &pvp) {
if (val != pvp.val) return val < pvp.val;
return pos < pvp.pos;
}
}x[MAXN];

struct Binary_indexed_tree {
int size = MAXN, c[MAXN + 10];
void add(int x, int v) { for (; x < size; x += (x & (-x))) c[x] = max(c[x], v); }
int query(int x) { int v = 0; for (; x; x -= (x & (-x))) { v = max(v, c[x]); } return v; }
void clear() { memset(c, 0, sizeof(c)); }
}bit;

int a[MAXN], b[MAXN << 1];
int n, q, m;

int idx(char str[]) {
int l = strlen(str), p = l;
for (int i = l - 1; i >= 0; i--) {
if (str[i] == '{' || str[i] == '(' || str[i] == '[') {
p = i;
}
}
int res = 0;
for (int i = 1; i <= p - 1; i++) {
if (str[i] == '.') continue;
res = res * 10 + str[i] - '0';
}
res += (str[0] - 'A' + 1) * 10000000;
return res;
}

int main() {
freopen("Piano.in", "r", stdin);
freopen("Piano.out", "w", stdout);
scanf("%d%d", &n, &q);
char str[30];
for (int i = 1; i <= n; i++) {
scanf("%s", str);
a[i] = idx(str);
x[i].val = a[i];
x[i].pos = i;
}
sort(x + 1, x + n + 1);
for (int t = 1; t <= q; t++) {
bit.clear();
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", str);
b[i] = idx(str);
}
int ans = 0;
for (int i = 1; i <= m; i++) {
Pos_val_pair p; p.pos = 0, p.val = b[i];
int pos = lower_bound(x + 1, x + n + 1, p) - x;
if (x[pos].val != b[i]) continue;
int f = bit.query(x[pos].pos - 1) + 1;
f = max(f, bit.query(x[pos].pos));
ans = max(ans, f);
bit.add(x[pos].pos, f);
}
printf("%.6lf\n", (double)ans / m);
}
return 0;
}

2.Movie

离线处理。
莫队算法+线段树
莫队算法:参考 莫涛的论文《Hose 解题报告》
直接分块解决
考虑两个任意位置的区间 [L1,R1][L1,R1][L2,R2][L2,R2],
已经维护好了前者的信息, 要得 到后者的信息,
只需要插入或删除 (L1,L2](L1,L2](R1,R2](R1,R2] 中的数。
具体来说,
L1<L2L1 < L2,则删除 [L1,L2)[L1, L2) 内的数,
L1>L2L1 > L2,则插入 [L2,L1)[L2 , L1) 内的数。
R1<R2R1 < R2,则插入 (R1,R2](R1,R2] 内的数,
R1>R2R1 > R2,则删除 (R2,R1] 内的数。
无论是哪种情况,所需的操作数总是 L1L2+R1R2|L1−L2| + |R1−R2|
将序列分成 M\sqrt M 块,以 LL 所在块为第一关键字, RR 为第二关键字排序。
然后按排序之后的顺序处理序列。
所以时间复杂度为 O((M+Q)M)O((M +Q)\sqrt M )
(线段树维护最长连续区间是经典算法就不多说了。)
时间复杂度: O((M+Q)Mlog2N)O(( M +Q)\sqrt M \log_2 N )

3.Gift

咕咕咕


2020.10.18

T1:约瑟夫问题

f(n,k)f(n,k) 表示 nn 个人,每 mm 个人删掉的答案

对于 f(n,k)f(n,k) 而言,在一圈内可以删掉 $\left\lfloor \dfrac{n}{k} \right\rfloor $个,剩 nnkn-\left\lfloor \dfrac{n}{k} \right\rfloor 个人,此时在 nkk\left\lfloor \dfrac{n}{k} \right\rfloor \cdot k 个人的位置,即 nnmodkn - n \mod k。于是我们继续递归处理,但是要还原相对位置,从 n(n mod k)n-(n\ mod\ k) 还原到 00 的位置,位置 resres 减去 nk\left\lfloor \dfrac{n}{k} \right\rfloor。同时还要考虑每 k1k-1 个人就要补上一个之前删掉的人,下标加上 resk1\left\lfloor \dfrac{res}{k-1} \right\rfloor

1
2
3
4
5
6
7
8
9
10
11
12
int josephus(int n, int k) {
if (n == 1) return 0;
if (k == 1) return n - 1;
if (k > n) return (josephus(n - 1, k) + k) % n; // 线性算法
int res = josephus(n - n / k, k);
res -= n % k;
if (res < 0)
res += n; // mod n
else
res += res / (k - 1); // 还原位置
return res;
}

T2:

trick:

1j<kn(ajbkakbj)2=(k=1nak2)(k=1nbk2)(k=1nakbk)2\sum_{1\le j<k\le n}(a_jb_k-a_kb_j)^2=\left(\sum_{k=1}^n{a_k}^2\right)\left(\sum_{k=1}^n{b_k}^2\right)-\left(\sum_{k=1}^na_kb_k\right)^2

用 BIT 维护。

Provement:

1j<kn(ajbkakbj)2(1)\sum_{1\le j<k\le n}(a_jb_k-a_kb_j)^2\qquad(1)

观察一下,发现 jjkk 相等时,(ajbkakbj)2(a_jb_k-a_kb_j)^2的值是零,所以 (1)(1) 乘二的结果就是

1j,kn(ajbkakbj)2(2)\sum_{1\le j,k\le n}(a_jb_k-a_kb_j)^2\qquad(2)

考虑化简

1j<kn(ajbkakbj)2=12(1j,kn(ajbkakbj)2)=12(1j,knaj2bk2+1j,knak2bj221j,knajbkakbj)=12(21j,knaj2bk221j,knajbkakbj)=1j,knaj2bk21j,knajbkakbj=(j=1naj2)(k=1nbk2)(j=1najbj)(k=1nakbk)=(k=1nak2)(k=1nbk2)(k=1nakbk)2\begin{aligned}\sum_{1\le j<k\le n}(a_jb_k-a_kb_j)^2&=\dfrac{1}{2}\left(\sum_{1\le j,k\le n}(a_jb_k-a_kb_j)^2\right)\\&=\frac 12\left(\sum_{1\le j,k\le n}{a_j}^2{b_k}^2+\sum_{1\le j,k\le n}{a_k}^2{b_j}^2-2\sum_{1\le j,k\le n}a_jb_ka_kb_j\right)\\&=\frac 12\left(2\sum_{1\le j,k\le n}{a_j}^2{b_k}^2-2\sum_{1\le j,k\le n}a_jb_ka_kb_j\right)\\&=\sum_{1\le j,k\le n}{a_j}^2{b_k}^2-\sum_{1\le j,k\le n}a_jb_ka_kb_j\\&=\left(\sum_{j=1}^n{a_j}^2\right)\left(\sum_{k=1}^n{b_k}^2\right)-\left(\sum_{j=1}^na_jb_j\right)\left(\sum_{k=1}^na_kb_k\right)\\&=\left(\sum_{k=1}^n{a_k}^2\right)\left(\sum_{k=1}^n{b_k}^2\right)-\left(\sum_{k=1}^na_kb_k\right)^2\end{aligned}

QED.


T3:

期望题,咕咕~


2020.10.24

试卷上写不要文件,测的时候又要,真的坑


总结:考试时大部分时间在划水,如果多点时间推,T1 没准就推出来了


T1

暴力

T2

打表

T3

没看

T4

放弃


result: 50pts


2020.11.28

color

想了好久,没想出来,就写一个自以为正确的构造,跑了。
结果只有 10 pts

发现只有 2 是偶质数,mod4\bmod 4 构造颜色,刚好能干掉限制条件

很多次没考虑到跟取模有关的构造方法了。

array

想出来了贪心考虑给定最小值的情况,但是需要改变式子形态:将填满的位置而不是最小值分离出来,才能推出正解。

直接二分斜率乱搞能得 95 pts

query

考虑把路径拆成两条链。

把一条链上的式子写出了,将与某个特定变量有关的全部移到式子的一边,方便处理。

dep(u)=dep(v)+vdep(u) = dep(v)+v

查询 u 至 lca 上的点每个点 x ,满足 dep(x)+x=dep(u)dep(x)+x=dep(u) ,于是可以用主席树维护一下每个点到根节点路径上的所有点的 dep+iddep+id 值(预处理)。
从 lca 至 v 的点 x ,满足 dep(x)+dep(u)2×dep(lca)=xdep(x)+dep(u)-2\times dep(lca)=x 维护 iddepid-dep

查询时在两颗主席树上查 dep(u)dep(u)dep(u)2×dep(lca)dep(u)-2\times dep(lca)

network

gugugu~~