之前写在本地的,丢上来测试一下
1. Educational Codeforces Round 56
E
题意:映射后即为,给一个排列,支持两种操作:询问区间[lb,rb]内权值在[la,ra]内的数字个数,交换两点的值。
key:树套树
树状数组套主席树,空间吃紧。注意到历史版本没有被别的继承并且不再访问,所以可以回收这些点的空间。
F
题意:给一个序列,每个点可以放权值在[1,k]中的数,序列上有些已经填好了。问没有连续len个数相同的方案数。
\(len\le n\le 10^5,k\le 100\)
key:dp,滑窗
对于每种权值k维护一个滑窗,分别是以i结尾长度为连续的0,1...len-1个k的方案数。每次转移时:
若当前是-1,则可以放一个k(长度为0...len-1变成1...len),或者放一个别的(长度为0),后者是所有滑窗的总和减去当前滑窗。
否则,若是x,则对于x则长度为0...len-1的变成1...len,长度为0的方案数是0。对于非x的滑窗只剩下长度为0的,方案数是x的滑窗之和。
于是只需要维护所有滑窗的和和每个滑窗的和即可。注意弹出尾部。复杂度\(O(nk)\)
2. Educational Codeforces Round 54
B
题意:n每次减去它的最小质因子,问操作几次变成0.
key:数学
偶数则一直-2,奇数则减去它的最小质因子(必定为奇数),之后一直-2.
E
题意:给一棵树,每次操作把ui节点子树内距离ui<=di的点+xi,问最终每个点点权。
key:dfs,离线,树状数组
转化成深度在区间内的点+一个数,dfs+树状数组,回溯时删去对应区间。
F
题意:一个序列被分为n份,每份内有ai个A类物品和bi个B类物品,排成一排。问是否存在一种安排方案使得没有连续k+1个相同的物品。
key:贪心,讨论
显然的贪心,转化为上一份最后是resa个A类物品或者上一份最后是resb个B类物品,再安排当前份中的物品,要使得转以后的resa和resb最小。分别考虑后取优。
假设X和Y类物品,上一份最后剩余dx个X类物品。需要考虑这几类情况:
- 无解。即X非常多或者Y非常多。假设X非常多,则此时是每连续放k个X然后再放一个Y,此时仍然会有非法情况。
- 不可能剩下X或者Y。假设不可能剩下Y,则此时是每连续放k个X然后再放一个Y最后Y用完时X剩余的个数<=k。
- X或者Y剩下1个。即一般情况,二者的数量差距并不大。
分别考虑后就能AC。
G
题意:二人博弈,游戏为:现有序列 \(b_1,b_2...b_k\) ,在长度为k的序列上,初始在1并且\(b_1-1\),之后若在x,则玩家的目标点为\(y\in[x,min(k,x+m)]\),要求目标点的\(b_y>0\),并且移动后\(b_y-1\)。
给一个长度为n的序列,支持两个操作:区间+di或者询问若以某一区间博弈的赢家。\(n,q\le 2*10^5,m\le 5\)
key:博弈,线段树
由于可以原地踏步,并且移动到该点则该点的权值-1(奇偶性反转)。根据有向图游戏,当前点若本来是偶数则必胜;否则若之后m个有必败态则必胜,没有则必败。
于是可以进行转移。考虑线段树维护:加一个偶数相当于没加,奇数相当于反转。每个区间维护:对于当前区间右端点之后m个态的2^m种取值,每种得到的结果是多少(也要存最开始的m个态来转移)。然后就可以区间合并了。维护反转操作,只需要初始化时记录一下即可。\(O(n*2^mlogn)\)
3. CodeCraft-19 and Codeforces Round #537 (Div. 2)
D
题意:给一个字符串,由至多52种字符组成,每次询问两个字符x和y,问有多少种重排方案使得所有同类字符在前一半或者后一半,并且x和y必须在同一半。\(n \le 10^5\) 且为偶数
key:组合数学,01背包
首先统计每种字符的出现次数,记为\(t_i\)。先不考虑x和y,对于一种合法方案,需要选出一个子集使得其和为n/2,它的排列方案是\((n/2)!/\prod t_{k_i}!\),剩下的形式相同,所以对于一种选取方法,对应的方案数是
\[ W=\frac{(n/2)!*(n/2)!}{\prod_{i=1}^{52}t_i!} \]
所以最终答案为2*W*d,其中d是选取一个无序集合使得其和为n/2的方案数。
对于该问题,即最终答案为2*W*d,其中d是选取一个无序集合使得其和为n/2且x和y必须被选的方案数。
对于计算d,这其实是一个01背包计数问题,可以处理出方案数,之后再退掉两个物品即可。复杂度\(O(52^2*n)\),因为常数小故可以通过。
Claris提出一种优化:由于我们只关注f[n/2]的答案,所以退掉第一个物品 i 是52*n,退第二个物品 j 时,对\(t_j\)相同的一起处理。改变f[n/2]的元素只有至多\(f[(n/2)/t_j]\)个,所以复杂度是\(O(52\times (n/2/1+n/2/2+...+n/2/52))=O(26nlogn)\)
E
题意:给一棵树,每次询问指定树根\(r_i\),给一个点集\(\{a_i\}\),要将其划分成至多\(m_i\)组,使得每组不存在两个点是祖先和孙子的关系,问方案数。\(n\le 10^5,m_i\le 300,\sum|a_i|\le10^5\)
key:dp,dfs序,lca
考虑一组询问:把点集按dfs序排序,\(f[i][j]\)为前i个点划分成j个集合的方案数,则
f[i][j]=f[i-1][j-1]+f[i-1][j]*(j-h[node[i]])
其中h[x]为给定点集中x的祖先个数。
可以发现这个dp顺序只要满足x在x的所有祖先计算之后再计算就可以了(因为只需要考虑x和x的祖先的约束),所以可以按照bfs序排序,即x与根的距离,也就是h数组。
对于多组询问,我们只要计算出h数组,然后对点集排序,然后dp就可以了。而计算h相当于计算点到根的路径上有多少个被标记点,这个可以用dfs序实现。由于动态,所以需要树状数组。
复杂度为\(O(300n+nlogn)\)
4. Codeforces Round #536 (Div. 2)
AK了,三发罚时有点可惜:数组开小+1,思路错+1,没考虑特殊情况+1
E:dp
F:离散对数(BSGS,原根)+矩阵乘法+exgcd
5. Codeforces Global Round 1
D
题意:给n个物品,每个物品有一个权值,每次可以拿走形如[x,x+1,x+2]或者[x,x,x]的三元组,求至多拿走多少个。\(a_i,n\le10^6\)
key:dp
可以注意到若拿三个[x,x+1,x+2]可以将其分为拿三个[x,x,x],所以可以认为对于每一个x,[x,x+1,x+2]至多拿两次。
考虑dp:\(f_{i,j,k}\)表示拿到权值为i的物品,拿了j个[i-1,i,i+1],k个[i,i+1,i+2]的最大个数,枚举[i+1,i+2,i+3]拿多少个转移即可。
注意不能对当前和上一个物品的个数dp,这样可能会漏掉一些状态。
E
题意:给序列a和序列b,每次可以把\(a_i\)变成\(a_{i-1}+a_{i+1}-a_i\),问能否把a变成b。
key:差分序列
这个之前在知乎上看到过……每次变换相当于交换差分序列相邻两项,所以只需要判断a和b的差分序列是否相同,还需要判断a和b序列的第一项是否相同。
F
题意:一棵树上点的标号是由 dfs 序顺次标,每条边有权值,q 次询问点 \(v_i\) 到标号在 \([l_i,r_i]\) 的叶子的最短距离是多少。 \(n,q\le5*10^5\)
key:dfs序,dfs,线段树
标号的性质:一棵子树的节点是连续的。或者可以认为点标号 = dfs 序标号。
考虑离线:每次处理 \(v_i\) 相同的所有询问。
首先处理出来所有叶子到 1 的距离。然后对树做 dfs ,每次从 u 到 v 时,设边权是 w,查询点到 v 子树内的叶子的距离减少 w,到 v 子树外的叶子的距离增加 w。于是用线段树维护区间加和区间最小值即可。
G
题意:给一颗树,树上有些点已经涂了 W。W 和 B 二人轮流涂色,W 先,率先涂出相邻三个 W 或者 B 的人赢,问谁赢或者平局。 \(n\le 10^5\)
key:讨论
显然 B 不可能赢,所以就看 W 是否能赢。相当于在树上下三子棋,B 的最优策略就是在 W 下完之后立马占领它的相邻点,否则必输。
大概分几种情况讨论讨论就行了,下面是 W 赢的情况(n>3,小于的情况特判):
- 已经存在连续两个 W。
- 两个 W 中间空一格。
- 存在大于等于四度的点。
- 三度点,且其中至少两个相邻点不是叶子。
- 已经被涂上 W 且不是叶子。
- 已经被涂上 W 且是叶子,且相邻点的度数大于等于 3。
排除以上情况,树的形态只能是一条链然后端点是三度点,所以还剩下三种情况:一条链,一条链且其中一个端点是三度点,一条链且两个端点都是三度点。对于染色情况,只会在非三度点的端点染色。
一条链:当且仅当两个端点都是 W ,且 n 是奇数。
一条链且其中一个端点是三度点:非三度点的端点是 W,且 n 是大于等于 6 的偶数。
一条链且两个端点都是三度点:n 是大于等于 7 的奇数。
前六种手玩即可。容易知道对于后三种,最终胜利的子局面一定是情况 2 或者情况 6 ,观察即可得。
H
题意:定义一个数字串是 modest 当且仅当其在 [l,r] 之间且没有前导 0。构造一个长度为 n 的数字串,使得其 modest 的子串数目最多,多种答案求字典序最小。 \(l\le r \le 10^{800} , n \le 2000\)
key:AC 自动机,dp
以下用正则表达式来表达一个字符串。[x-y] 表示匹配一个在 x 到 y 范围内的字符,{k} 表示重复匹配 k 次。
首先严格定义一个 modest 串。当 \(|l| \ne |r|\) 时, 一个串 S 是 modest 串当且仅当其被以下三个正则表达式之一匹配:
- \(l_0 l_1 l_2 \dots l_{x - 1} q [0-9]\{|l| - x - 1\}\) ,其中 \(q > l_x\)
- \(r_0 r_1 r_2 \dots r_{x - 1} q [0-9]\{|r| - x - 1\}\) ,其中 \(q < r_x\)
- \([1-9][0-9]\{x - 1\}\) ,其中 \(|l| < x < |r|\) 。
按长度分类易知一个串至多只被其中一个正则表达式匹配。当 \(|l|=|r|\) 时和上述相似。
我们称 \([0-9]\{k\}\) 是一个长度为 k 的通配符。于是将以上三个正则表达式去掉所有的通配符剩下的前缀拿出来,建立 AC 自动机(一共有 \(O((|l| + |r|) \cdot \Sigma) + 9\) 个字符串),然后将每个字符串对应的通配符存到对应的节点上。该问题就变成一个 AC 自动机上的 DP。
从 \((i,v)\) 经过数字 \(d\) 转移到 \((i+1,u)\) 时,需要将 \(u\) 对应的字符串的贡献加上。易知贡献为以 u 结尾的所有字符串的所有通配符中,长度小于等于 \(n-i-1\) 的个数。换句话说,就是 u 所对应的 fail 树的祖先节点中通配符长度小于等于 \(n-i-1\) 的个数。这个东西可以预处理。总复杂度为 \(O((|l| + |r|) \cdot n \cdot \Sigma ^ 2)\) ,其中 \(\Sigma\) 是字符集大小,即是 \(10\) 。
补充一句,为了方便输出答案,可以添加一个 “0” 的字符串,用来处理答案有前导0的情况。
6. Codeforces Round #538 (Div. 2)
AK了,都不算难。
F:支持区间乘x,询问区间积的欧拉函数值,权值<=300。
300以内的素数有62个,压位后变成询问区间积和区间或,线段树即可。
7. Educational Codeforces Round 59
C
主要是看错题了,虽然耽误了一个小时,但是感觉看错后的题比较有意思。下面说看错了的题意。
题意:给一个字符串,每个点有一个权值 \(a_i\) 。选择多段区间,每段区间中任意字符出现次数要<=k,区间权值是每个元素的权值和。问所有区间权值和最大是多少。
key:dp,线段树
对于点 i ,若选择了它,则可以根据 k 和字符串处理出最小的 \(pr_i\le i\) 使得 \(str(pr_i,i)\) 不合法。
记 \(f_{i,0/1}\) 表示前 i 个,第 i 个不选/选的权值和的最大值。则有如下式子:
\[ f_{i,1}=f_{j,0}+S_i-S_j \ , \ j \in [pr_i-1,i-1] \\ f_{i,0}=\max\{f_{j,1}\} \ , \ j
然后就是一个线段树维护的 dp。
D
题意:给一个 n*n 的 01 矩阵 A,定义将矩阵 A 进行 \(x\)-压缩 为矩阵 B 当且仅当 x 是 n 的因数且对于任意的 i 和 j, \(A[i][j] = B[\lceil \frac{i}{x} \rceil][\lceil \frac{j}{x} \rceil]\) 成立。问 x 最大是多少。
key:暴力,复杂度
其实做法很简单,只是复杂度当时算错了…
题意换句话说就是,将 A 切分成等大的若干个正方形,使得每一个正方形内元素相同,问正方形边长最大是多少。
第一种做法:预处理前缀和,于是可以 \(O((\frac{n}{x})^2)\) 进行check,总复杂度 \(O(\sum_{x|n} \frac{n^2}{x^2}) = O(n^2*\sum_{x|n} \frac{1}{x^2}) = O(n^2)\) 。因为 \(\sum_{n=1}^{\infty} \frac{1}{n^2}=\frac{\pi^2}{6}\) 。
第二种做法:可以发现当 x*p 合法时,x 也合法。于是可以对 n 分解质因数,然后枚举幂来check。此时若不用前缀和,复杂度是 \(O(n^2logn)\) ,但这个 log 特别小所以也没什么问题。如果用前缀和,那么复杂度和上面一样。
E
题意:给一个 01 串,每次可以选择相同且连续的一段,记长度为 \(i\) ,将其消去,并将剩下的两个串连接,此时得分为 \(a_i\) 。问若干次消除后的最大得分。 \(n\le100\)
key:区间dp
区间 dp。因为有可能连出来多个连续相同字符而不把整个区间同时消除,所以状态要比普通的区间 dp 多记录一些信息。
设 \(f_{c,l,r,k}\) 为将区间 \([l,r]\) 消到剩余 k 个字符 c 的最大得分。于是有如下式子:
\[ f_{c,l,r,k}=\max\{f_{c,l,i-1,0}+f_{c,i+1,r,k-1}\} \ ,\ s[i]=c,\ l\le i\le r,\ k\ge 1 \\ f_{c,l,r,0}=\max\{\max\{f_{0,l,r,k}+f_{1,l,r,k}\}+a_k\} \]
第一个式子是枚举剩余的 k 个字符 c 的第一个出现位置在哪里,此时需要把左边全部消掉,右边剩余 k-1 个字符 c。
复杂度 \(O(n^4)\) 。实现时要注意非法状态和枚举顺序(写记搜比较好)。
F
题意:n 份贷款,第 i 份在买入时将有 \(a_i\) 的收益,此后每个月都将损失 \(b_i\) ,直到 \(k_i\) 个月之后停止。每个月至多只能买入一份贷款,每份贷款至多只能买一次,问合理安排后在某一天的最大收益是多少。 \(n \le 500\)
key:二分图最大权匹配(KM算法)
必然只在1~n天买入。假设第 i 份贷款在第 j 天买入,则在第 e 天的收益是 \(a_i-\min(e-j,k_i)*b_i\) 。于是假设在第 n 天收益最大,则可以得到第 i 份贷款在第 j 天买入的收益。于是变成二分图最大权匹配问题。
二分图最大权匹配,用 KM 算法是 \(O(n^3)\) 可以通过。用网络流是 \(O(k*spfa)\) ,这里是一个完全二分图,所以跑的巨慢无比…
G
题意:给数列 \(c_i\) 和递增数列 \(d_i\),定义一个区间 \([l,r]\) 的权值是 \(\sum_{l\le i\le r}a-c_i + \max_{l\le i\le r-1}(d_{i+1}-d_i)^2\) ,问区间权值最大是多少。 \(n \le 3*10^5\)
key:单调栈,线段树
算法1
枚举左端点 l,考虑每一个 r 的答案。l 每减少 1,对于每一个 \(r \in [l,n]\) 都增加了 \(a-c_l\) 。此时需要尝试用 \((d_{l+1}-d_l)^2\) 去更新每一个 \(r\in[l+1,n]\) ,相当于与原先的值比较大小,若新值比它更大,则替换掉并更新答案。
前者可以用线段树区间加和 RMQ 直接维护,后者不可以直接维护(因为新值替换掉的旧值的答案不一定是最优解)。因为每一个 \((d_{l+1}-d_l)^2\) 所管辖的区间是一定的,于是新值替换掉的旧值也是一定的。用单调队列维护 \((d_{l+1}-d_l)^2\) ,每次新值覆盖旧值时直接把旧值从对应区间减去,最后再把新值加入。由于每一个 \((d_{l+1}-d_l)^2\) 至多插入一次、删除一次,所以总复杂度是 \(O(nlogn)\) 。
算法2
这里是题解的做法,对 \((d_{l+1}-d_l)^2\) 排序,记为 \(a_i\) 。枚举 \(a_i\) ,每次处理它的贡献,即处理跨过它的区间。可以用 set 找到左右区间,用 RMQ 来处理询问。 \(O(nlogn)\) 。
算法3
本质即为区间和+最大相邻差值的平方,而第二个可以直接单调栈,而区间和可以用前缀和做,所以可以用单调栈实现一个线性做法。
在做单调栈的同时枚举右端点,并记录相邻差值的平方取 \(a_i\) 时 \(sum_l\) 的最小值,于是就可以直接更新。
8. Codeforces Round #533 (Div. 2)
E
题意:m 个点,指定 n 个点集使其两两连边,求最大独立集。 \(m \le 40\)
key:状压搜索,复杂度
这个题主要是看错题意…
显然可以做一个 meet in the middle。这里说一个题解给的很奇妙的做法,复杂度是 \(O(2^{m/2})\) 。
定义 \(solve(S)\) 为点集为 \(S\) 时的最大独立集,则每次枚举 \(p \in S\) 。
- 不选择 p ,则答案为 \(solve(S \ xor \ 1 << p)\)
- 选择 p ,则 p 的邻居都不能被选,则答案为 \(solve(S \ and \ G_p)\) ,其中 \(G_p\) 是与 p 不相邻的点集。
关于复杂度为什么是对的,题解是这样解释的:
Consider the recursion, there are at most \(m/2\) recursion calls before we arrive into the state, where there are no set bits of the first half.
我理解的是,两种情况都会把 p 删去,第二种情况删的更多,最坏时第二种情况每次只删掉 1 个,于是每次删去 1 个或者 2 个元素,复杂度就是 \(O(2^{m/2})\) 了。
*9. Codeforces Round #534 (Div. 1)
C
题意:给一个每个点度数 \(\ge 3\) 的无向连通图,给定 \(k\),求要么找出一条长至少为 \(\frac{n}{k}\) 的链,要么找出恰好 \(k\) 个环使得每个环的长度大于 \(3\) 且不是 \(3\) 的倍数且每个环上都存在一个点在其它输出的环上没出现过,要么输出 \(-1\)。 \(k\le n\le2.5*10^5 \ , \ m\le5*10^5\)
key:构造,定理
先以 \(1\) 为根建一棵 dfs 树。
定理:深度 \(\le\frac{n}{k}\) 的树至少有 \(k\) 个叶子。
证明:设有 \(c\) 个叶子,设第 \(i\) 个叶子到根经过的点数是 \(x_i\) ,则有 \(\sum x_i \ge n\) ,抽屉原理有 \(\max\{x_i\}=deep \ge \frac{n}{c}\) 。
于是若深度 \(\ge \frac n k\) ,则找到一条合法的链。
否则,考虑每个叶子 v:由于度数至少是 3,所以至少存在两条返祖边,设其连向 \(x\) 和 \(y\) ,此时得到三个环,长度分别是 \(d(v,x)+1 \ , \ d(v,y)+1 \ , \ d(x,y)+2\) 。其中必定存在一个的长度不是 3 的倍数,于是得到一个环,而点 v 只会出现在这个环上。
不存在无解情况。
D
题意:给 n 个数 \(a_i\) ,对于每个数可以除掉它的一个不超过 k 的因子,花费为 \(c_i\) ,目标是让 \(a_i\) 的 gcd 为 1。若对下标集合为 \(I\) 的所有数做了操作且达成目标,求 \(|I|*\sum_{i\in I}c_i\) 最小。 \(n\le10^6\ ,\ a_i,k\le10^{12}\)
key:素数,dp,剪枝
先求出所有 \(a_i\) 的 gcd,再分解质因数,得到的素数个数一定 \(m \le 11\)。之后只需要关注这些素数即可。若对一个数进行操作,一定是直接消除某些素数,所以至多选 \(m\) 个数。所以只要根据 \(k\) 处理出每个数可以消除的素数集合,就得到一个 \(O(n\cdot m \cdot 3^m)\) 的 dp ,显然过不了的。接下来就是这个题的神奇之处:
剪枝 1
将不是目标素数的素数全除掉得到一组新的 \(a_i\),然后把相同 \(a_i\) 压缩起来,对应的所有 \(c_i\) 只保留最便宜的 \(m\) 个,那么至多只剩下 \(O(M*m)\) 个物品(而不同的 \(a_i\) 只有 \(M\) 个),其中 \(M < 12000\) 。
\(M\) 取最大时,一定是取最小的 \(m\) 个素数,因为 \(a_i\le10^{12}\) ,可以通过程序暴力得到:
1 39
2 469
3 2369
4 6734
5 10808
6 11598
7 7815
8 3462
9 895
10 110
11 4
左边是 \(m\) ,右边是对应上界 \(M\) 。现在复杂度变成 \(O(M\cdot m \cdot 3^m)\) ,仍然不能接受。
剪枝 2
因为最终至多只选 m 个,大部分被选中的机会很少甚至根本不会被选,这由它们每个管辖的素数集合和花费有关。可以预处理 \(ok[S]\) 表示素数集合为 \(S\) 时那些数可能被选,而对于每个 \(S\) ,至多存储 \(m\) 个最便宜的数即可。然后按数字顺序做 dp。
这样所有的转移方向至多只有 \(m*2^m\) ,再枚举当前选了几个数,总的转移复杂度是 \(O(m^2*3^m)\) 。
于是步骤是:
- 对 gcd 分解质因数。
- 压缩 \(a_i\) ,用每个\(a_i\) 的 \(c_i\) 预处理 \(ok[S]\) ,此处复杂度是 \(O(M\cdot2^m\cdot m)\) 。
- dp。 \(f_{i,S}\) 表示选了 \(i\) 个数,当前消除的素数集合是 \(S\) 的花费最小是多少,转移复杂度 \(O(m^2\cdot 3^m)\) 。
E(待补)
题意:给出 \(n\) 个数,从中连续选出 \(n\) 个数(可重复选)计算它们的 radix sum,求对所有的 \(i \in [0,n)\) radix sum 为 \(i\) 的方案数。
radix sum 定义:设 a 和 b 的 radix sum 为 \(s(a,b)\) ,则 \(s(a,b)=(a_1+b_1)\mod 10, \ldots ,(a_k+b_k)\mod 10\) ,其中 \(a,b\) 的数位分别是 \(a_1...a_k,b_1...b_k\) 。即十进制下的不进位加法。多个数的 radix sum 定义:\(s(t_1, \ldots ,t_n)=s(t_1,s(t_2, \ldots ,t_n))\) 。 \(n,x_i< 10^5\)
key:多项式
10. Educational Codeforces Round 60
C
题意:二维平面上指定起点和终点,再给出周期为 n 的风向变化,每单位时间可以选择一个方向移动一单位长度或者停止不动,并且随风走 1 单位长度(方向是上下左右)。问到终点的最短时间。 $n \le 10^5 , sx,ex,sy,ey \le 10^9 $
key:二分
主要是想了很久却没想到二分……
t 时间内能到的充要条件是:起点经过 t 时间内风的偏移量后距终点的曼哈顿距离 \(\le t\) 。因为每单位时间都可以向终点逼近一步。所以二分一下答案就行了……
E
题意:这是一个交互题。对一个字符串按顺序进行若干次操作,每次操作形如:把位置在 \(a_i\) 和 \(b_i\) 的字符交换。现在给定操作后的字符串 \(s\) ,你至多可以询问三次,每次给出一个字符串然后返回该字符串操作后得到的字符串,输出 \(s\) 操作前的字符串。 \(|s| \le 1000\)
key:交互,思维
即想知道:第 \(i\) 个位置的字符操作前在哪个位置,记为 \(d_i\) 。
可以考虑如此细分:若第 \(i\) 个位置操作后是字符 \(c\) ,则 \(d_i \in c\) 出现过的所有位置。下一次再将 \(c\) 出现过的所有位置细分成 26 个字符,则 \(d_i\) 的取值范围变成原来的 \(\frac 1 {26}\)。由于 \(1\cdot26\cdot26<10000,1\cdot26\cdot26\cdot26>10000\) ,所以只需要三次即可。
举例来说,设字符集为 3,三个字符串可以取:
aaaaaaaaabbbbbbbbbccccccccc
aaabbbcccaaabbbcccaaabbbccc
abcabcabcabcabcabcabcabcabc
F
题意:给一个字符串,设字符集为 \(p\) ,再给出 \(p\cdot p\) 的对称二元矩阵 \(A\)。定义一个字符串是 crisp 当且仅当对于任意两个相邻字符 \(i,j\), \(A[i][j]=1\) 。对于初始字符串,每次操作可以删除某个字符所有出现位置并将剩余部分拼接起来,要求每次操作后得到的字符串都是 crisp 的。问若干次操作后得到的字符串长度最短是多少。 \(p \le 17, n \le 10^5\)
key:高维前缀和
只要预处理出来两个不可相邻字符 a,b 中间有哪些字符出现了,那么如果 a 和 b 没有被删掉,但中间出现的字符都删了,那么就不合法。统计这个只需要扫一遍即可。
之后就需要一个高维前缀和,将所有满足 a 和 b 没被删,中间出现的都被删了,其他随意的状态全部标记,之后跑一个 dp 即可。
高维前缀和:
for(int k = 0;k < m;k ++)
for(int l = 0;l < (1<> k & 1)
tmp[l] |= tmp[l^(1<
即一维一维地算前缀和。
G
题意:给一个长度为 n 的排列,定义一段区间的权值为:这段区间形成的笛卡尔树,每个点的子树大小的和。q 次询问一个区间的权值。 \(n,q\le 10^6\)
key:线段树
先只考虑左子树大小的和,右子树对称。
其实就是统计有多少 \(a_i>a_j,j 的点对个数。
所以可以处理出来每个点影响的区间,然后离线按左端点排序。先把所有区间插入(即区间+1),扫的时候删除,答案即为对应的区间和。
*11. Codeforces Round #539 (Div. 1)
C(待补)
题意:你有个数字 \(v\) 和每秒增量 delta,即平均每时刻过去都要将 \(v\) 加上 delta(此处时间是连续的)。q 次操作,每次为:在 \(t_i\) 时刻将 delta 修改为 \(s_i\) 、删除在 \(t_i\) 时刻的事件、询问在 \([l_i,r_i]\) 时刻初始值为 \(v_i\) 时第一次变为 0 的时间点。 \(q \le 10^5\)
D
题意:n 个点的带标号树,边权取值是 [1,m] ,问有多少个不同的树满足标号 a 和标号 b 两点的距离恰好等于 m。 \(n,m\le10^6\)
key:计数,定理
Cayley's formula 的一般化:\(n\) 个带标号点形成 \(k\) 棵树的方案数为 \(T_{n,k}=k*n^{n-k-1}\) 。
枚举 a 和 b 之间的距离 k ,则其中有 k-1 个点。隔板法可得这条链上点和边的方案数是 \(\binom{m-1}{k-1}\cdot\binom{n-2}{k-1}\cdot(k-1)!\) 。
剩余的边权值随意,点的分配可以看做所有的 n 个点形成 k+1 个森林,然后把链上的 k+1 个点选出来连上。所以总方案数是:
\[ \binom{m-1}{k-1}\cdot\binom{n-2}{k-1}\cdot(k-1)!\cdot m^{n-k-1} \cdot T_{n,k+1} \]
E
题意:给 n 个数,支持区间乘、单点除(保证整除)、求区间和,需要模 mod(不一定是素数)。 \(n,q,x_i\le10^5\)
key:线段树,数论
唯一的问题在于单点除时可能没有逆元。考虑将 mod 唯一分解,而 \(10^5\) 内的数唯一分解后素数不超过 7 个,设素数向量为: \(p_1\dots p_m\)。
需要在线段树的叶子节点维护:该点权值除去每个素数后剩余部分模 mod 的值、每个素数的幂次。单点除时直接更新。
对于区间乘,除了对答案的修改需要打标记下传以外,还需要下传叶子所维护的信息。
这样复杂度是 \(O(7nlog^2n)\) ,预处理快速幂后复杂度是 \(O(7nlogn)\) 。
F(待补)
*12. 2018 China Collegiate Programming Contest Final
CDEFHJ
K
题意:求 a 使得 \(a^{2^{30}+3} \equiv c \mod n\) 。其中 n 是相邻两素数乘积。 \(n,c \le 10^{18}\)
key:数论
两边同时 \(1/(2^{30}+3)\) 次方,得到 \(a = c^{d} \mod n\) 。其中 d 是 \(2^{30}+3\) 在模 \(\phi(n)\) 下的逆元。快速幂即可。
13. 2018 Arab Collegiate Programming Contest
单挑 9 题舒服啊,再给十几分钟就 10 题了,水题欢乐赛真开心(
K
题意:构造一个长度为 n 的数列,每个元素取值范围在 [L,R] 之内,且模 3 为 0 的子段和个数恰好为 k,问方案数。 \(n,k \le 10^4\)
key:dp,数学推导
取值范围无所谓,可以看做填 0/1/2 。
若有解,则 k 一定是 \(O(n^2)\) 的一个东西。假设做出来的结果中,前缀和对 3 取余为 0,1,2 的个数分别是 \(x,y,z\) ,则必须满足
\[ x+\frac {x*(x-1)}{2} + \frac {y*(y-1)}{2}+\frac {z*(z-1)}{2}=k \]
然后发现有解的 n 至多是 245 。
然后就很简单了,直接用上面的变量之间作状态: \(f[i][j][k][l]\) 表示长度为 i ,前缀和对 3 取余为 0,1 的个数分别是 i,j ,当前 i 个数总和对 3 取余是 l 的方案数。
*14. 2017-2018 ACM-ICPC, Asia Tsukuba Regional Contest
DEGHJK
E(待code)
题意:给一个长为 n 的序列 a,每个点为黑或白,每次操作可以将一个长度小于等于 k 的区间涂白或者涂黑,问变成序列 b 的最少操作次数。 \(n \le 5*10^5\)
key:dp,单调队列
考虑 dp。 \(f_i\) 表示前 i 个格子一样的最少操作次数。考虑转移。可以枚举 j<=i ,把 [j,i] 全涂成 i 点的颜色,然后再将 [j,i] 区间内的点涂成一样的。后者发现是区间内黑白的段数。
用单调队列扫一遍即可。
G(待code)
题意:在一个边长为 1 的四面体上, a 和 b 两点都在某一顶点 A,它们要沿某一面的某一方向移动长度 x,y,穿过棱后方向不变,问最终是否在同一个面上。
key:讨论,六边形坐标系
把四面体展开,复制到整个平面,发现是在一个夹角为 60°,每个单元格是正三角形的坐标系内,判断一下即可。
*15. 2018-2019 ACM-ICPC Brazil Subregional Programming Contest
AHIJM
A
题意:n*m的点阵,问两点连线不经过其他点且长度在 [L,R] 区间内的点对数。 \(n,m,L,R\le 10^5\)
H(待code)
题意:给一个字符串 S,给一个树,每个节点上有一个字符。需要支持单点修改、询问 u 到 v 路径上形成的字符串中 S 出现了多少次。 \(|S| \le 100,\ n,q \le 10^5\)
key:树剖,线段树,hash
树剖转区间,维护 hash 值。 \(O(n|S|log^2n)\)
J
题意:平面上给 n 个点,其中 k 个关键点,询问最小欧几里得距离生成树。其中每个关键点至多连接一个非关键点。 \(n\le100, \ k\le \min(n,9)\)
K
题意:给 n 个圆,每个圆都包含原点,没有三圆交于一点也没有相切。询问交点个数,若大于 2*n 则输出 greater。 \(n \le 1.5\cdot 10^5\)
M
题意:给一个 M 个语句、n个变量的或与表达式,每个语句至多包含三个变量。每个语句中要么一个变量为真要么三个变量为真,问合法的指派中字典序最大的是多少。 \(M,n \le 2000\)
*16. 2018-2019 ACM-ICPC, Asia Seoul Regional Contest
HI
E:二分,坑题,需要判x=0时必须分在第一份,不能划分到第二份。
C
题意:给 n 个圆,需要把它们放到一条线上,保证对于任意排列只存在相邻两个圆相切(即最大半径与最小半径的比值小于4)。问所有排列中总长度最小是多少。 \(n \le 1000\)
key:构造,打表找规律
对于一个排列,总长是第一个的半径+最后一个的半径+2*相邻两个的半径乘积再开根号。
打表(dls写了个随机+遍历所有排列+输出解……一眼就看出规律)可找出规律。
按半径排序后,n 是偶数时唯一,n 是奇数时只有可能是两种情况。
G
题意:给定 \(s,w_a,w_b\) ,求积分
\[ \int_0^s \rm dx ( \int_x^{\min(x+w_a,s-w_b)}w_b \ \rm dy \ + \ \int_{\max(x,s-w_b)}^{\min(x+w_a,s)}s-y \ \rm dy) \]
key:数学
讨论,展开。避免浮点数所以要*6。
LL work(LL s,LL wa,LL wb,LL wc) {
LL tmp = 6*(s - wa - wb) * wa * wb;
tmp += 6*wa * (s - wb) * wb;
tmp -= 6*wb * (pf(s-wb) - pf(s-wa-wb)) / 2;
LL ans = 0;
if(wa >= wb) {
LL ans2 = 0;
ans += 6*(pf(s-wa) - pf(s-wa-wb)) / 2;
ans += 6*(wa + wb - s) * wb;
ans += 6*wb * (wa - wb);
ans += 6*s * wb;
ans -= 6*(pf(s) - pf(s-wb)) / 2;
ans2 -= 6*(lf(s) - lf(s-wb)) / 6;
ans2 += 6*pf(s-wb) * wb / 2;
ans2 -= 6*(pf(s)-pf(s-wb)) * (wa-wb) / 2;
ans2 -= 6*pf(s) * wb / 2;
ans2 += 6*(lf(s) - lf(s-wb)) / 6;
ans *= s;
ans += ans2;
}
else {
LL ans2 = 0;
ans += 6*(pf(s-wb+wa) - pf(s-wb)) / 2;
ans += 6*(wb - s) * wa;
ans += 6*(wb - wa) * wa;
ans += 6*s * wa;
ans -= 6*(pf(s) - pf(s-wa)) / 2;
ans2 -= 6*(lf(s-wb+wa) - lf(s-wb)) / 6;
ans2 += 6*pf(s-wb) * wa / 2;
ans2 -= 6*(pf(s-wa) - pf(s-wb)) * wa / 2;
ans2 -= 6*(wb - wa) * pf(wa) / 2;
ans2 -= 6*wa * pf(s) / 2;
ans2 += 6*(lf(s) - lf(s-wa)) / 6;
ans *= s;
ans += ans2;
}
return tmp + ans;
}
J
题意:无向图,有些点属于 A 类,剩余是 B 类。再标记某些点是 C 类,问是否存在 A 类的点 a 到 C 类 的点 c1,B 类的点 b 到 C 类 的点 c2,所形成的路径上的边权形成的序列相同。 \(n \le 1000,边权 \le 20\)
key:搜索
\(f[i][j]\) 表示某个 A 类点走到 i,某个 B 类点走到 j 是否可行,每次枚举边权转移即可。状态数是平方级别。
*17.Codeforces Round #532 (Div. 2)
D
题意:交互题。999*999的平面上有666个黑点,一个白点。每回合白点每次只能移到周围八个格子上;挑一个黑点移动到任意空白处。若白点和某个黑点同行或者同列那么白胜,2000回合后白不胜则黑胜。白先。你执白,要通过移动达成胜利。
key:交互
E
题意:给一个无向图带权,你需要反转一些边的方向使其没有环,问反转的最大边权最小是多少,并给出一个方案。 \(n \le 10^5\)
F
题意:给 n 个数,q 次区间查询,每次询问区间内选出一些点使其异或最大。 \(n \le 5*10^5\)
key:线性基
*18. Educational Codeforces Round 62 (Rated for Div. 2)
E
题意:长度为 n 的序列,每个点被填上了 [1,k] 的数字或者为空。一个合法的序列为它的任意长度大于1且为奇数的子区间不是一个回文串。问给空格子填上 [1,k] 的数字使其合法的方案数。 \(n,k\le 2*10^5\)
key:组合数学,dp
可以发现一个合法的序列是不存在长度为 3 的回文串。即将相邻奇数项不同且相邻偶数项不同。奇偶拿出来分别做。
现在问题变成了对于一个序列,前后可能有一个数字,问有多少种方案使没有相同数字相邻。前后没有数字就是 bzoj 的越狱。有一个也差不多,关键在于前后都有数字。
只需要设 \(f_{i,0/1}\) 表示长度为 i ,前后不相同/相同的方案数。讨论在一端填数字的状况即可转移。复杂度 O(n)
F
题意:二维平面上 n 次操作,支持插入一个点、删除一个点、求 \(E(S)\) 。其中 S 是点集, \(E(S)\) 定义为 S 中按规则加入若干点后得到的点集大小(只做询问不实际加入)。规则为:若已存在三个点构成一个平行坐标轴的矩形的三个角,则把剩下那个角的坐标加入点集。 \(n \le 10^5\)
key:线段树,带撤销并查集
新姿势。
先考虑给定点集如何求 \(E(S)\) 。考虑点 \((x,y)\) 何时被计算进答案的充要条件,当且仅当点集中既有横坐标为 x 的点也有纵坐标为 y 的点。换句话说,若将横坐标相同和纵坐标相同的点之间连边,得到的每个联通块的答案是联通块内不同的横坐标个数*不同的纵坐标个数。
进一步发现,若将横纵坐标看成点,每个点看做对应横纵坐标之间连一条边,则答案就是每个联通块内横坐标点数*纵坐标点数。所以可以用并查集维护。
问题在于如何删除。可以预处理出来每条边出现的时间段,对时间建一棵线段树,线段树每个点存储覆盖该区间的边,然后对其dfs,进入则插入边,回溯则删除边,到叶子计算答案。于是只需要带撤销并查集即可。复杂度 \(O(nlogn\alpha(n))\) 。
*19. 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest
BFHL
C
题意:二人在树上博弈,初始所有点都无色。A 选一个点染粉,B 选一个点染棕,A 再选一个点染粉,然后游戏结束。此时 A 的得分为到达棕点路径上存在粉点的点数, B 想最小化,A 想最大化,问最终 A 的得分。 \(n \le 10^5\)
key:树上博弈,树的重心
先让 A 的第一步随便选一个点 u,此时 B 会挑 u 的一个子树的重心 v, A 会将其最大的儿子砍掉。此时得到一个解。
所以关键在 A 的第一步到底选的哪个点。由于 A 想最大化得分,所以在此基础上要趋向于得分更多的策略。若 A 将第一步选择的点向远离 v 的方向移动,则 B 保持原来的策略,得分只会变小(因为经过第一个粉点的点变少了,而第二个粉点至多弥补)。故 A 只会把 u 向当前的 v 靠近。
于是有一个分治思路:对第一个选的点分治,O(n) 得到当前点的答案。由于至多分治 log 次,总复杂度 \(O(nlogn)\) 。
E
题意:给两个长度为 n 的 01 串 s 和 t,每次操作是将连续长度为 m 的 1 或 0 翻转。问 s 是否能经过若干次操作后变成 t。 \(len \le 10^6\)
key:智商
首先发现操作是可逆的。
然后发现实际上操作是将连续 m 个 1 变为 0 或者将一个 1 连续走 m 步,并且不能越过其他 1。
于是可以将两个串的 1 都向前挪,达到 m 个就消去,最后检查是否相同即可。
M
题意:给两个串 s 和 t,要求选择 s 的一个子串与 t 的一个前缀首尾相连,使其是回文串,问方案数。 \(len \le 10^6\)
key:manacher,z算法
将 s 倒过来,即求 s 的一个子串与 t 的一个前缀相同,并且子串前面紧跟一个回文串。用 z 算法做一遍匹配,枚举回文串,该回文串的贡献就是以 [i+1,i+r[i]] 作为开头与 t 的前缀匹配的字符串数,这个可以前缀和一下。于是总复杂度是 O(n)。
*20. 2018-2019 ACM-ICPC, Asia Nakhon Pathom Regional Contest
BI
A
题意:给 n 个数,每个数表示一个杆子的高。可以从一个高杆子跳到低杆子上,途经和终点的杆子高度不能超过起点。每次询问从 x 开始最多可以跳多少次或者从 x 跳到 y 最多可以跳多少次。 \(n \le 10^5\)
key:笛卡尔树
如果数字互不相同,显然就是笛卡尔树的子树深度/深度差。考虑相同数字。
笛卡尔树可以建成相同数字组成一条右儿子链。
需要维护的东西是一个点到根经过的不同数字最多是多少,记为 \(ans\_from\_root[u]\) 。还需要维护从该点向下经过不同数字最多是多少,记为 \(ans\_from\_this[u]\) 。前者很好维护,后者需要维护一个 \(tmp[u]\) 表示 u 子树中 \(ans\_from\_this[u]\) 最大是多少。这样如果是从和当前点权值相同的儿子转移而来(一定是右儿子),则需要用右儿子的左儿子的 tmp 来更新 \(ans\_from\_this[u]\) ;否则直接用 \(tmp[u]\) 更新。
询问从 x 开始最多可以跳多少次,直接输出 \(ans\_from\_this[u]\) 。否则,设 y 在 x 的子树内。x 不能跳到 y,当且仅当有一个与 x 点的高度相同的杆子挡住,即 x 的右儿子和 x 权值相同且 y 在这一分支之内。
讨论清楚之后,一遍dfs即可。
*21. 2018-2019 ACM-ICPC, Asia Dhaka Regional Contest
ADGI
G
题意:简化后为给一棵树,多次询问 u 到标号在 [l,r] 的点集中最近的距离是多少。
key:点分树
建出点分树,套个线段树,按标号排序,维护距离最小值。
22. 2018 German Collegiate Programming Contest (GCPC 18)
差个 G ,快乐。
23. Codeforces Global Round 2
F
题意:给一棵带权树,定义 f(x) 为删去若干边后使得每个点度数小于等于 x 且删去边集权值和最小是多少。求 f(0) 到 f(n-1)。 \(2\le n \le 2.5*10^5\)
key:树形 DP,堆
考虑单组询问: \(f_{u,0/1}\) 表示将 u 子树中删去若干边,并且节点 u 的度 \(\le x\) / \(\le x+1\) 的答案。对于转移,每次考虑删掉哪些边,可以按 \(val_v+f_{v,1} - f_{v,0}\) 排序选择最小的若干个,剩下的不删。
对于多组询问,考虑按 x 升序做。每次只考虑度数 > x 的点,设其是 good。每次只考虑 good 点形成的森林。
所有点的度数之和是 2*(n-1)。所以对于所有的 x ,good 点总数就是 2*(n-1) 。
对森林里每棵树做 dp。u 的儿子可能是 good 也可能不是 good。对于 good 的点,按单组的做法排序;对于不是 good 的点,直接按边权排序。每次就是拿出权值最小的若干个转移。对于树边可以直接遍历,非树边可以用一个权值线段树维护(插入一个点,查询前 k 小的权值和)。
当 x 变大时,只有可能 good 点变成非 good ,所以将其丢到线段树即可。
总复杂度 \(O(nlogn)\)
G
题意:有 n 个人,需要打 m 个阵营,每个阵营有 \(a_i\) 个血,你需要先把 n 个人划分成 m 个小队,每回合指定某个小队攻打某个阵营(可以多打一),每个阵营减血量是攻打他的人数之和。目标是让敌方每个阵营的血量小于等于 0。构造出一组划分方案使得回合数最少,并输出每回合攻打策略。 \(1\le m\le n \le 10^6\)
key:构造,智商
回合数至少是 \(\frac{\sum a_i}{n}\) 上取整,考虑什么时候会取到这个数。首先把所有的 \(a_i\) 对 n 取模。
考虑攻打第一个,最优情况下我方恰好有某些小队总人数就是 \(a_1\) ,剩下的攻打第二个,那么我方剩下的恰好有某些小队总人数就是 \(a_2\),直到最后一个。若前面的都恰好凑出来,则最后一个不需要恰好,此时回合数就是上面的那个值。
可以设 a 的前缀和对 n 取模,排序后取差分。此时每个需要恰好凑出来的数字都对应了一个区间,并且按下标首尾相接,于是从第一个开始攻打即可。
H
题意:给 n 个数组,每个数组中有 x 个 \(a_i\) ,y 个 \(b_i\) ,z 个 \(c_i\) 。要求每个数组中取出恰好一个数将其异或起来。问得到 i 的方案数是多少。求所有的 i 从 0 到 \(2^k-1\) 。 \(n \le 10^5 ,k \le 17\)
key:FWT
fwt 复习题。
首先一个暴力做法是构造 n 个长度为 \(2^k\) 的数组,做 n 遍 fwt,其中每个数组中只有至多三个非零元素: \(F_i[a_i]=x,F_i[b_i]=y,F_i[c_i]=z\) 。下面开始复习 fwt。
因为 fwt 是线性变换,即变换后的每个元素是变换前的 \(2^k\) 个元素的线性组合。由于本题只有三个非零元素,并且是异或的 fwt ,所以线性组合的系数是 1 或 -1。
fwt 对异或的变换为 \(f_i = \sum_j (-1)^{count(i\&j)}a_j\) ,其中 \(count(x)\) 为 x 的二进制中 1 的个数。
若变化为 \(F_i[0]=A,F_i[b_i\ xor\ a_i]=B,F_i[c_i\ xor\ a_i]=C\) ,则可以发现每个数组经 fwt 后只有四种元素:\(A+B+C,A+B-C,A-B+C,A-B-C\) 。最终答案需要异或所有 \(a_i\) 作映射。
于是对于第 i 位,若以上四种元素在所有数组的第 i 位中出现次数分别是 \(x,y,z,w\) ,则答案为 \((A+B+C)^x*(A+B-C)^y*(A-B+C)^z*(A-B-C)^w\) ,现在的问题是确定 \(x,y,z,w\) 。
其实若 \(F[0]=A,F[b]=B,F[c]=C,F[c\ xor\ b]=D\) ,则由 fwt 对异或的变换可知变换后的数组只包含 \(A+B+C+D,A+B-C-D,A-B+C-D,A-B-C+D\) 这四种元素。
设 \(B=1,A=C=0\) 、 \(C=1,A=B=0\) 以及 \(F_i[b_i\ xor\ c_i]=1\) 可得三个线性无关方程,以及显然的 \(x+y+z+w=n\) 。现在有四个方程,可以解出每一位的 \(x,y,z,w\)。最后再做一遍 fwt 逆变换即可。
其实这个做法很容易拓展,可以出 m 个非零元素,或者运算不是异或。
*24. 2018-2019 ACM-ICPC Southeastern European Regional Programming Contest (SEERC 2018)
A
D
题意:给一颗树,需要从 1 遍历每条边至少一遍之后回到 1,可以花费权值的时间走边或者花费时间 k 跳到另一个点上。至多跳 m 次,问花费时间最少是多少。 \(n,m \le 1000\)
key:树形dp
如果不跳,那么一定是每条边走两遍。
如果跳,那么可以认为跳过的路径上的所有边都只走了一遍,剩下的是两遍。手玩可得,如果一条边被覆盖了两次那么还是要走两遍。换句话说,被覆盖奇数次的边走一遍,偶数次的边走两遍。无论怎样,都会存在一条回路使得其能回到 1。
于是一次跳只关心两个端点的位置,不妨设其为 good 。
\(f_{i,j}\) 表示 i 子树内有 j 个 good 点的最小代价。若 j 是奇数那么 i 的父边计算一次,偶数则计算两次。做完之后再加上跳的代价即可。
H
题意:n 个人, m 个愿望,每个愿望形如 x 希望 y 不开心。x 开心当且仅当他希望的不开心的人中有一个真的不开心。你需要实现至少 m/4+1 个愿望,保证有解,输出一种方案。 \(n \le 10^5 , m \le 2*10^5\)
key:随机
随机每个人开不开心然后check,可过,飞快。
K
题意:每次在二维平面上添加一个点或者一个矩形,每次操作后问有多少个 <点,矩形> 对满足点被矩形包含。 \(n \le 10^5\)
key:cdq 分治
把矩形转化为前缀,就是二维偏序问题,直接上 cdq 分治。
25. Educational Codeforces Round 61
E
题意:有一个容量为 W 的背包,有 8 个物品,体积为 i 的物品有 \(c_i\) 个, \(1 \le i \le 8\)。问背包最满可以装多满。 \(0 \le W \le 10^{18}, 0 \le c_i \le 10^{16}\)
key:背包
取 1 到 8 的 lcm,设为 L,其值为 840。
发现如果一个物品 i 拿的数量大于等于 L/i ,则可认为是拿了一个体积为 L 的物品,于是每个物品只需要选小于 L/i 个,剩下的都凑成 L 。
于是只需要枚举每种物品拿了多少个,剩下的全部凑成 L 的物品。 \(f_i\) 表示拿了总共为 i 的体积的物品,剩下的凑成的 L 的物品最多是多少,做一个 01 背包即可。 i 需要取到 8L。
*26. 2018-2019 ACM-ICPC, Asia Jiaozuo Regional Contest
BCGKL
J
题意:给 n 个矩形,覆盖在 \(m \cdot m\) 的二维平面上。删掉恰好两个矩形,使得被覆盖的点数最少,求最小值。 \(n \le 3*10^5, m \le 1500\)
key:骚操作
考虑所有被覆盖一次和两次的点,答案只有可能是覆盖它们的矩形。
现在问题在于如何找到覆盖当前格子的矩形是哪些,该点至多被覆盖两次。
维护两个前缀和:子矩阵加 i ,子矩阵加 i*i 。这样就知道了 \(a+b\) 和 \(a^2+b^2\) 的值,解方程即可。
*27. 2012-2013 ACM-ICPC, NEERC, Moscow Subregional Contest
BDH
J
题意:给 n 个物品排序,权值不满足传递性,要求相邻两个满足偏序关系。每次可以询问两个元素谁大谁小,要求总询问数不超过 10000. \(n \le 1000\)
key:交互,排序
相当于 cmp 函数调用次数不超过 10000 。sort不是稳定排序,归并是稳定的,所以需要用 stable_sort。这样排出来即使不满足传递性也是合法的。
28. Forethought Future Cup - Elimination Round
E
题意:有 n 个数,每次操作形如:把所有大于/小于 x 的数字乘 -1。问 m 次操作后的最终序列。 \(n,m \le 10^5\)
key:线段树
开值域线段树,每个点维护该值是否被翻过。按大于/小于号和 x 的正负四种情况讨论,发现只需要维护区间翻转和区间赋值即可。
G
题意:有 n 个位置,每个位置可以设定一个高度,最高为 h。一个位置的高度为 x 则可以获得 \(x^2\) 的价值。有 m 个区间,每个区间有一个设定高度,若区间内存在一个位置的高度超过设定高度,则需要付出 \(c_i\) 的代价,求总价值和最大。 \(n,h \le 50\)
key:区间 dp,最小割
其实是两个算法都能做……
区间 dp
\(f_{i,j,k}\) 表示区间 \([i,j]\) 最高高度小于等于 k 的最大价值。每次枚举中间点,认为它的高度就是 k,然后枚举被完全包含在该区间的限制,复杂度 \(O(n^5)\) 。
最小割
首先转补集,即不选的高度最小。
每个位置拆 h 个点表示选的高度, \((i,j)\) 连向 \((i,j+1)\) 流量为 \(h^2-j^2\) 表示选高度为 j 的价值。设一个限制为 \([l,r]\) 最大高度超过 x 则付出 c 的代价,则新建一个点 p ,所有区间内的高度为 x+1 的点向 p 连正无穷的边, p 向汇点连 c 的边。源点再跟所有高度为 0 的点连边。
对于正确性,考虑割的情况:如果割了某个限制的边,则表明有权值大于等于这条边的位置,即区间内已经有超过了高度为 x 的限制,那么它会继续向后。 若割了某个 \(h^2-j^2\) 的边,表明该位置的高度小于等于 j 。
*29. Codeforces Round #554 (Div. 2)
F1 F2
D
题意:求长度为 2*n 的所有合法括号序列形成的 trie 中最大边独立集大小,取模。 \(n \le 1000\)
key:dp
由于要取模,所以这个题不能直接dp,先找一下规律。
贪心策略:设根节点为 0 ,只拿以奇数层点为父亲的边。
考虑从下到上。由于叶子只在最后一层,那么如果有一个奇数层的点不选,那么肯定选它上面的一个点,这对上面造成的限制会更大,答案不会变优。
E
题意:有一个长度为 n 的序列 a ,构造 b 和 c 序列为:a 的相邻两项的最大值/最小值,长度为 n-1 。然后再按同样的排列对 b 和 c 作置换。现在给 b 和 c,求 a。 \(n \le 10^5\)
key:欧拉路
\(b_i\) 和 \(c_i\) 一定相邻,所以连边跑欧拉路即可。不合法就判一判。
30. Codeforces Round #553 (Div. 2)
F
题意:求无向图中最少留下多少条边使得整个图是双连通图。输出方案。 \(n \le 14\)
key:状压 DP
双连通图的组成:一个双连通图和一个点/一条链连起来。所以预处理链,然后转移,复杂度 \(O(n^23^n)\) 。
31. Codeforces Round #551 (Div. 2)
F
题意:在 [0,l] 之间随机选 n 个线段(每个线段的随机方式是随机两个实数端点),问被覆盖大于等于 k 次的总长度期望是多少。 \(n,k \le 2000\)
key:DP
只需要考虑在 [0,1] 之间随机。被覆盖大于等于 k 次的总长度期望即为再随机选点 P 落在被覆盖大于等于 k 次的点的概率。由于实数上随机,所以可以认为所有点互不相同。现在问题变为点 P 被覆盖大于等于 k 次的概率是多少。
\(f_{i,j,x}\) 表示放了 i 个点,当前还有 j 个左端点没有合口,P点放没放的概率。每次考虑当前点放哪种点即可。最终答案为 :
\[ \frac{f_{2*n+1,0,1} * n! * 2^n}{(2*n+1)!}*l \]
*2018-2019 XIX Open Cup, Grand Prix of Korea
BCJK
D
题意:给 n 个人排队,要求第 i 个人必须在区间 \([l_i,r_i]\) 内,且给定 m 组 \(u_j\) 必须在 \(v_j\) 之前的关系。 \(n,m \le 3*10^5\) 。
key:拓扑排序
显然对于每个 \((u_j,v_j)\) 的关系,他们的区间边界满足 \(l_{v_j}=\max(l_{v_j},l_{u_j}+1)\) ,\(r_{u_j}=\min(r_{u_j},r_{v_j}-1)\) 。拓扑排序之后可以对每个点做一次这种操作。对于这样得到的区间,考虑这样的贪心:从左往右扫,每次选可选的区间中右端点最靠左的。这样保证当有限制关系的区间同时可选时,一定选了前驱,这样排出来的就是正确的。
G
题意:有 n 个格子,每个格子有一个灯,每个灯打开有一定花费,一盏灯可以照亮它本身和它的前后一格。你可以交换 k 次两个灯的位置,求照亮所有格子的最少花费。 \(n\le2.5*10^5, k\le 9\)
key:DP
考虑交换后的最终序列,它的最优解中打开的那些灯。可以发现在最优解中,一定不存在交换了两个灯,使得两个灯最终都灭/都亮,所以一定是交换了若干对,每对在最优解中一个灭一个亮。
所以只需要挑出至多 k 个灯设定它们是灭的,再挑至多 k 个灯设定它们是亮的,剩下的普通 dp 即可。
\(f_{i,a,b}\) 表示前 i 个灯,已经挑出了 a 个亮的灯,b 个灭的灯,且第 i 个灯是亮的的答案。每次转移到下一个亮的灯 \(j\ (j \le i+3)\) ,需要讨论 j 的灯需不需要挑出来、中间夹着的灭的灯需不需要挑出来。
最后计算答案,需要在末尾添加一个 INF 的灯,再添加一个 0 的灯。这样是目的是保证所有点都考虑过是否挑出来了。
M
题意:带边权的树,挑出恰好 k 条边使其互相没有交点且边权和最大。 \(k
key:树形 DP,wqs 二分
设恰好选 y 条边的最大权值是 \(f(y)\),容易发现 \(f(y)\) 的差分随着 y 增大而减小,即形成了一个凹函数(上凸)。此时便可以用 wqs 二分。
给每条边加一个代价 x,发现选的边数与 x 的大小成正比。于是二分 x,做 O(n) 的 DP 即可。
要注意两点:当有多解时,选选的边更多的方案。在这个前提下,当没有结果恰好是 k 条边时也无妨,答案需要是第一个 >=k 的值减去 k*mid 。
下面简单说一下证明。二分 X 得到的是 f(y)+yX 。若不存在结果恰好是 k 条边的情况,则必有 \(f(k)+kX=f(l)+lX\) ,其中 \(l\) 是二分得到的满足选边大于 k 的最小选边数。此时用等式右边减去 kX 就是所求答案。
其实这种情况说明 \(f(y)\) 在 k 附近的斜率相同,都是 -X。此时加上一个斜率为 X 的直线会形成水平的情况。
J
题意:给 n 个数,求 \(F_{i,j}=(i-j+1)*\min_{i\in [i,j]}a_i\) 的第 \([L,R]\) 小是多少。 \(n \le 3*10^5, L_i \le R_i \le \frac{n(n+1)}{2}, R-L+1 \le 3*10^5\)
key:堆,求 k 小
求第 L 小:二分。此时知道了以每个点为最小值的最小区间长度是多少。将其加入堆里,每次取最小的。