NOI 选做

[NOI2011] NOI 嘉年华

考虑设 \(cnt_{l, r}\) 表示被 \([l, r]\) 包含的区间种类数。那么我们设 \(f[i][j]\) 表示考虑到第 \(i\) 段时间,第一个区间段有 \(j\) 个,第二个区间段最多可以有多少个。转移直接枚举新的一段到什么位置即可。复杂度 \(O(N^3)\)

考虑第二问,枚举必须选择的数,然后转移的时候钦定转移区间至少有一段包含枚举的 \([l, r]\),复杂度 \(O(N^4)\)。可以猜出其满足决策单调性,复杂度 \(O(N^3)\)

[NOI2011] 兔兔与蛋蛋游戏

有一个结论:由于所有网格图的回路长度都是偶数,要移动必须经过一个空格,那么回路上黑色格子和白色格子奇偶性不可能一样,所以空格子一定不会再次来到去过的地方。

那么直接爆搜 \(SG\),复杂度最多 \(O(2^{nm})\),可以拿到 \(70pts\)

再继续观察,我们走的路径一定是一个形如链状的结构,我们将相邻的黑白点连边,不难发现这是一个二分图,问题就变成了:给定一张二分图,不能走回头路,两人轮流操作,不能走的输,问先手是否必胜。

考虑先手遵循一个这样的策略:对于任意一个最大匹配,如果先手在未匹配的点上,那么后手一定会到达一个匹配的点上。此时先手前往后手选择点的匹配点上,以此类推。

假设后手可以走到一个非匹配点上,那么将起点到这个点的路径上的匹配边和非匹配边交换,那么最大匹配将加一,所以矛盾。

于是,先手只要最开始可以移动到一个点,使得其不在最大匹配中即可,也就是说,先手只要在一个不在所有最大匹配中匹配的点即可。判断的话直接忽略掉该点判断最大匹配即可。

[NOI2011] 兔农

这是类似于斐波那契的一种序列,因为 \(N\) 很大,所以不难猜出其有循环节。

如果 \(f[i]\%k=1\),那么我们把截止到 \(i\) 的数分成一组,那么每一组内部是一个斐波那契,且长度和结尾由第一个数确定。

考虑每一组的长度如何确定,假设开头为 \(x\),那么第 \(i\) 项的 \(\%k\) 的值为 \(x\times f_i\%k\),因此 \(f_i\)\(x\) 的逆元,因此一个 \(f_i\) 和一个 \(x\) 一一对应。

预处理一下然后不断递归即可知道循环节。知道循环节之后,那么我们可以推出转移矩阵,直接在膜 \(p\) 意义下转移这个矩阵即可。

还有一个问题,假设 \(p, x\) 不互质,那么没有逆元,此时循环节就在这一层内,同样处理即可。

[NOI2011] 阿狸的打字机

首先我们可以建出所有字符串的 \(AC\) 自动机,对于每次询问,首先可以将 \(y\)\(AC\) 自动机上暴力匹配,将相同的 \(y\) 一起处理,在每个位置打上标记,每次询问相当于是查询 \(fail\) 树上 \(x\) 到根节点标记点的个数,不难想到可以用树状数组维护,只需要支持子树加和单点查询即可。

对于相邻两个 \(y\),两者最多差一个字符,根据删除和插入讨论是跳父亲还是儿子即可。

[NOI2012] 美食节

知道这是网络流就比较简单了,从后往前表示贡献即可。但是直接做连边量是 \(O(nmp)\) 级别的,我们还需要优化。

接下来有一个很牛逼的操作,考虑到建图中,我们对于每个厨师做每一道菜都需要新建一个点(设为点对 \((i, j)\)),但是实际上,有很多点是没有作用的。那么我们可以这么做:先只加入 \((i, 1)\) 的点,进行增广路之后,如果 \((i, 1)\) 被增广了,此时我们再加入 \((i, 2)\),以此类推。此时点数就变成了 \(O(P)\),而边数也优化成了 \(O(pm)\),可以通过此题。

[NOI2012] 魔幻棋盘

先考虑一维情况怎么做,由于 \(gcd(a , b) = gcd(a, a - b)\) ,发现差分之后答案不变,因此我们将原序列差分,那么问题就变成了单点修改和区间 \(gcd\) 了,用线段树显然可以实现。

对于二维的情况,直接二维差分之后用二维线段树即可。

[NOI2013] 向量内积

先考虑 \(k=2\) 的情况,设 \(F(i, j)\) 表示 \((\sum_{x=1}^m a_{i, x}a_{j, x})\%2\),接下来是一步神奇的操作。我们考虑 \(\sum_{j<i}F(i, j)\),不难发现其为 \(\sum_{x=1}^ma_{i, x}\times (\sum_{k=1}^ia_{k, x})\)。当 \(F(i, j)\ne (i-1)\%2\) 时,至少有一个 \(j\),满足 \(F(i, j)=0\),枚举 \(j\) 即可做到 \(O(ND)\)

不难发现我们每次记录前缀和,找到合法的 \(i\) 之后 \(check\),总复杂度仍然为 \(O(ND)\)。考虑该算法的正确性,对于一组合法的 \((i, j)\),假设 \(i>j\),该方案不会被检测到当且仅当 \(i\) 前面排有偶数个合法的 \(j\),那么我们随机排列,每个 \(i\) 可以被检测到的概率是 \(\dfrac{1}{2}\),大概 \(check\) \(6\sim 8\) 次正确率就很高了,复杂度 \(O(Tnd)\)

再来考虑 \(k=3\) 的情况,沿用上面的思路,但此时 \(\%3\) 的值可以是 \(0/1/2\),直接对他求和没有什么意义。但是我们发现,如果我们求的是平方和,那么就转化为了上面的问题。所以我们只需要对每个 \(i\) 计算出 \(\sum_{k=1}^i(\sum_{x=1}^ma_{i, x}\times a_{k, x})^2=\sum_{k}\sum_{x}\sum_{y}a_{i, x}a_{k,x}a_{i, y}a_{k,y}=\sum_{x}\sum_{y}a_{i,x}\times a_{i, y}\times (\sum_{k}a_{k,x}\times a_{k,y})\),同样维护一下前缀和即可,复杂度 \(O(TND^2)\)

[NOI2013] 树的计数

\(dfs\) 序变成 \(1\sim n\),同时将 \(bfs\) 序也进行对应变化。\(bfs\) 序分层数即为层数变化,不难发现每一层都是单增的。

考虑 \(bfs\) 序中,如果 \(b[i] < b[i + 1]\),那么\(i\)\(i + 1\) 之间,他们的深度差不超过 \(1\) ,所以他们之间至多分一次层,又因为每一层单增的,所以如果 \(b[i] + 1 \ne b[i + 1]\) 那么 \(b[i], b[i + 1]\) 之间必须分一层,否则的话可分可不分。

我们将所有相邻且不单增的位置强行分层,可以证明只要满足上述两条件即为合法,由于保证合法,那么一段区间里面有且仅有一个不单增的位置,那么直接给这段区间打上 \(0\) 的标记,让答案加一即可,剩余没打上标记的位置可分可不分,贡献为 \(0.5\)。差分一下即可。

[NOI2015] 寿司晚宴

发现如果一个质因数大于 \(\sqrt{N}\),那么他最多只能出现一次。而小于 \(\sqrt{N}\) 的质数只有 \(8\) 个,因此我们可以采用状压 \(dp\)。设 \(dp[i][j]\) 表示第一个集合状态为 \(i\),第二个集合状态为 \(j\) 的方案数。

我们将所有数按照最大质因数排序,对于相同最大质因数的数,我们一起进行转移,对于同一组的数,只可能分配给一个人,枚举分给那个人即可。

[NOI2016] 优秀的拆分

首先题意可以显然的转化为求每一个点,往前/往后 \(AA\) 的数量。枚举 \(A\) 的长度 \(len\),一个神奇的操作是将字符串分组,每隔 \(len\) 个就分成一段,这样段数总数是 \(O(NlogN)\) 的。

对于相邻两段,求出其最长公共前后缀,这里可以采用后缀树或者直接二分 \(Hash\)

特判掉只跨过一段的,那么剩余的 \(A\) 一定跨过两段,根据相邻三段的最长公共前后缀讨论即可。

[NOI2016] 国王饮水记

首先比 \(1\) 号小的水我们一定不要,\(k\)\(n\) 大肯定无用。

将水位排序,显然每一次选择我们一定会选择 \(1\) 号和一段连续的区间联通。那么不难得到一个 \(dp\),即 \(dp[i][j]\) 表示考虑到了 \(i\),已经使用了 \(j\) 次操作,得到的最高水位,转移枚举一个 \(k\) 转移过来即可,复杂度 \(O(N^2KP)\),注意这里每次 \(dp[i][j]\) 每次需要取前缀 \(max\)

转移时枚举 \(j\),设 \(g[i] = dp[i][j]\)\(f[i] = dp[i][j - 1]\),转移式为:

\[g[i] = max_{k<i}(\dfrac{f[k]+sum[i]-sum[k]}{i-k}) \]

这是一个斜率优化的形式,看成是点 \((k, sum[k]-f[k])\) 到点 \((i, sum[i])\) 的斜率,直接在凸包上三分即可。也不难发现满足决策单调性,于是可以得到一个 \(O(NKP)\) 的做法。

根据打表可以知道,长度大于 \(1\) 的区间不会超过 \(log\) 次,所以我们只需要转移 \(log\) 层即可,复杂度 \(O(NlogKP)\)

[NOI2017] 整数

先考虑一个 \(O(NlogN*30)\) 的做法,考虑对于 \(a\) ,把他二进制拆分,先把对应位置进行暴力加减。然后我们只需要考虑进位和退位的事情。进位和退位只关心连续的一段 \(0/1\),再将其反转,于是我们只需要支持:单点修改,查询一个点后的第一个 \(0/1\) 即可。采用任何带 \(log\) 的数据结构均可。

考虑压位,将 \(30\) 个位置压成一位,对于每一位只维护其是否全 \(0\) 或者全 \(1\),找到后暴力修改即可,可以采用线段树简单实现。

[NOI2017] 泳池

恰好为 \(k\) 不好统计,考虑差分,统计最大面积不超过为 \(k\) 的概率。

首先每隔 \(k\) 个就至少会出现一个 \(0\) ,那么我们设 \(dp[i]\) 表示长度为 \(i\) ,且最大面积不超过 \(k\) 的概率,设 \(f[i]\) 表示长度为 \(i\) ,且不存在 \(0\) ,最大面积不超过 \(k\) 的概率。

那么不难得出 \(dp[i] = \sum_{j=1}^kdp[k-j]\times f[j]\),这是一个常系数齐次线性递推的模型,现在考虑怎么计算 \(f[j]\)

从高往低一次考虑高度,设 \(g[i][j]\) 表示长度为 \(i\),且最小高度为 \(j\) 的方案数,不难得出转移:

\[g[i][j]=\sum_{a+b+1=i}(\sum_{k\ge j} g[a][k])\times (\sum_{k>j}g[b][k])\times p^j(1-p) \]

注意为了防止算重需要保证枚举的是最后一个长度为 \(j\) 的。用后缀和优化一下即可,复杂度 \(O(K^3)\)。但根据调和级数,有大量无用状态,真是复杂度为 \(O(K^2logK)\)

常系数线性齐次递推:设数列 \(a\) 满足 \(a_i=\sum_{j=1}^kf_j\times a_{i-j}\),那么我们可以在 \(O(K^2logN)\) 或者 \(O(KlogKlogN)\)的时间计算 \(a_n\)

设多项式 \(F(x)=\sum_{j=1}^kf_j\times x^{k-j}\)\(G(x)=x^n\%F(x)\),那么我们有结论:\(a_n=\sum_{i=1}^ka_i\times g_i\)。所以我们只需要支持多项式取模即可。暴力实现多项式取模复杂度为 \(O(K^2logN)\)

[NOI2018] 冒泡排序

考虑到如果存在三个数 \(i < j < k\),满足 \(p_i>p_j>p_k\) ,那么一定至少存在一次交换没有卡满上界,于是问题可以转化成:有多少个排列,字典序小于给定排列,且满足最长下降子序列小于 \(3\)

由于要满足字典序的限制,按照通常套路我们一定是要枚举和给定串的最长公共前缀。那么我们只需要求出,前 \(i\) 位确定,有多少个排列最长下降子序列小于 \(3\)

实际上,我们只要知道了长度为 \(x\),第一位为 \(i\) 的排列数,那么我们可以映射到对应排列中去。具体的,第一位为我们当前枚举的字母,在剩余字母中的排名,然后根据排名依次填入即可。

所以我们只需要求出 \(f(m, i)\),即长度为 \(m\),第一位为 \(i\) 的所有排列个数即可。最后计算答案时,可以枚举 \(lcp\),枚举下一位填入什么,不难发现下一位可以填入的是一段区间内所有 \(lcp\) 内没有出现过的数,也就对应了 \(\sum_{i=l}^r f(m, i)\)。其中 \(r\) 是由于字典序的限制, \(l\) 是由于要大于第二个上升序列的最后一个元素。这些都可以很好的求出来,于是我们只用考虑怎么推出 \(f(m, i)\)

枚举第一个比 \(x\) 大的数,假设其位置为 \(i\),为权值 \(j\),那么不难发现 \(1\sim i\) 一定是一个单调递增,那么我们同样可以采取映射的方式,也就是把 \(1\sim i\) 没有出现过的数离散化。那么不难发现,一对 \((i, j)\) 的贡献为 \(f(m - i + 1, j - i + 1)\)。于是我们可以写成:

\[f(m, x)=\sum_{2\le i\le x + 1 \le j \le m} f(m - i + 1, j - i + 1) \]

考虑 \(f(m, x)\)\(f(m - 1, x - 1)\) 的关系,不难发现

\[f(m, x)=\sum_{i=x-1}^{m-1}f(m-1,i)=f(m-1,x-1)+f(m, x + 1) \]

需要注意 \(f(m, 1)=\sum_{i=1}^{m-1}f(m - 1, i)=f(m, 2)\),所以可以 \(x=1\) 时仍然成立。

于是 \(f(m, x)\) 就有了组合意义:你当前在 \((1, 1)\),每次可以走 \((1, 1)\) 或者 \((0, -1)\),到 \((m, x)\) 的方案数(纵坐标不能走到 \(0\))。

把坐标轴旋转,\((x, y)\to (x, x - y)\),那么就相当于每次可以走 \((1, 0)\) 或者 \((0, 1)\),到达 \((m, m - x)\) 的方案数,且不能经过直线 \(y=x\),这个显然是 \(\dbinom{2 * m - x}{m-x}-\dbinom{2*m-x}{m-x-1}\),于是我们可以 \(O(1)\) 计算。至于需要求出 \(f(m, x)\) 的后缀和,直接就是 \(f(m + 1, x + 1)\)

[NOI2018] 你的名字

先建出原串的后缀树,再对每个询问串建出后缀树,先求出本质不同子串数量,然后考虑用总的减去出现过的本质不同的字符串的数量。

考虑用在 \(S\) 的后缀树上进行匹配,对于每个 \(l\),求出一个最大的 \(r\),使得其在 \(S\) 中出现过,那么在 \(T\) 的后缀树上标记这些子串,\(dfs\) 一边即可求出有多少个子串出现过。

先考虑 \(l=1,r=n\) 的情况,直接枚举 \(l\) ,每次 \(l\) 增大时跳 \(fail\),不断跳儿子知道不能匹配为止。而对于 \(l\ne 1, r\ne n\) 的情况,只需要用线段树合并判断一下 \(endpos\) 集合即可。

[NOI2019] 斗主地

首先有一个暴力 \(dp\),设 \(dp[i][j]\) 表示第 \(i\) 轮第 \(j\) 个位置上的期望,转移比较显然,每个 \(dp\) 值转移的复杂度为 \(O(j)\)

对于 \(type=1\) 的点,打表后不难看出其答案为等差数列,故我们只要知道了首项,那么就可以推出整个 \(dp[i]\)\(dp[i][1]\) 是可以快速求的,因此我们可以 \(O(M)\) 计算答案。

对于 \(type=2\) 的点,根据前面的结论,我们可以猜测其为二次函数,那么我们只需要维护出 \(dp[i][1\sim 3]\) 即可,这些都是可以快速维护的。维护之后重新计算二次函数的各项常数即可。

[NOI2019] 序列

首先假设我们确定了相同部分,那么我们一定是选择剩余的 \(a, b\) 集合中前 \(k - l\)。所以 \(a, b\) 的前 \(k - l\) 大的数一定是会被选择的。

那么我们先选择前 \(k - l\) 个,那么如果对于一个 \(i\),既选择了 \(a_i\),又选择了 \(b_i\),那么这一个 \(i\) 一定会被选择。不断重复这个流程,直到选择了 \(k-l\) 个不同的 \(a_i, b_i\)

此时我们需要凑出一对 \(a_i, b_i\),那么有三种情况:

1.选择一对没有选择过的 \(a_i, b_i\)
2.找到一个选择过的 \(a_i\),选择其 \(b_i\),再找到一个没有选择过的 \(a_i\)
3.找到一个选择过的 \(b_i\),选择其 \(a_i\),再找到一个没有选择过的 \(b_i\)

我们可以建出其费用流图,费用流有一个重要性质,就是从源汇点连出或者连入的边一定不会再走其反向边,也就是说,我们可以由 \(k-1\) 的答案直接推出 \(k\) 的答案。所以我们直接选出这三种情况的最大值更新即可。

[NOI2019] 机器人

首先考虑一个朴素 \(dp\),设 \(f[i][j][k]\) 表示考虑到区间 \([i, j]\),区间最大值为 \(k\) 的方案,\(s[i][j][k]\) 表示 \(\sum_{x\le k}f[i][j][x]\),转移比较显然,也就是枚举区间最大值的位置,而可行的位置只有三个,所以我们可以得到一个 \(O(N^2K)\) 的做法。

\[dp[i][j][k] = \sum_{|(a-i)-(j-a)|\le 2}s[i][a - 1][k] \times s[a + 1][j][k - 1] \]

发现可能有用的区间实际上不多,实际上打表出来之后只有 \(2500\) 个,因此我们直接记忆化搜索即可。可以拿到 \(50pts\)

如果我们将 \(f[i][j][k]\) 视作一个关于 \(k\) 的多项式,不难发现 \(s[i][j][k]\) 也是一个关于 \(k\) 的多项式,转移的时候相当于取两者的点值然后进行相乘,那么仍然是一个多项式!

考虑到 \(f[i][i]=x^0\),不妨采用归纳证明多项式系数,发现其为一个 \(r-l\) 次多项式。那么我们只需要 \(n\) 个点值就可以还原出 \(f(1, n)\)。对于 \(a_i=1, b_i=10^9\) 的情况,直接差值即可,复杂度 \(O(N^2W)\)\(W=2500\)

考虑 \(a_i\ne 1, b_i\ne 10^9\) 的情况,此时 \(f[i][i]\) 不再为 \(x^0\),其只有在自身值域内点值为 \(1\),否则为 \(0\)

考虑 划艇 的做法,我们将值域离散化,也就是将值域拆成若干段 \([l_i, r_i)\),显然段数是 \(O(N)\) 级别的。而对于每一段,差值的做法又是满足的。对于每一个值域段,小范围跑暴力,大范围跑差值,

[NOI2020] 命运

首先考虑朴素 \(dp\),设 \(f[i][j]\) 表示考虑到以 \(i\) 为根的子树,子树内的所有还未有 \(1\) 的链最的深度。

转移即考虑当前位置填或者不填,假设 \(v\)\(u\) 的新儿子,不难得到转移:

\[f[u][i]'=f[u][i]\times \sum_{j\le i}f[v][j]+f[v][i]\times \sum_{j<i}f[u][j]+f[u][i]\times \sum_{j\le dep[u]}f[v][j] \]

不难发现每次转移之和前缀相关,采用线段树合并即可。

[NOI2020] 超现实树

做法非常的简洁…

我们定义好树:找到原树的最长链,一棵树是好树,当且仅当最长链上所有点,除去最长链中包含的那个儿子,大小不超过一。

不难发现不是好树的树是没有意义的,因为假设一个点存在两个大小超过 \(1\) 的儿子,那么其一定无法表示一个为 \(1\) ,另一个为一棵子树的所有树,这种树是有无穷多个的。

定义一棵树是几乎完备的,当且仅当其子树是几乎完备的,对于一个节点,我们考虑四种情况,如果四种情况全在则为几乎完备的。

  1. 只有左儿子,我们递归左儿子。
  2. 只有右儿子,我们递归右儿子。
  3. 有左右儿子,且左儿子大小为 \(1\),我们递归右儿子。
  4. 有左右儿子,且右儿子大小为 \(1\)

这四种情况全部存在,且递归下去全部是几乎完备的,那么这棵树是几乎完备的。考虑采用反证法,很好说明如果有一种情况不是几乎完备的,则整个树林就不是几乎完备的。

posted @ 2021-06-02 22:53  呢没理他  阅读(100)  评论(0编辑  收藏  举报