BZOJ题目(持续更新)
bzoj1009:kmp想法+递推+矩阵快速幂。很好的想法,考虑用长串去kmp匹配短串,dp[i][j]表示匹配指针分别指在i、j位置时候,前i位母字符串一共有多少种可能性,那么dp[i][j]=Σdp[i-1][k]*p[k][j] p[k][j]就是状态k后加一位数字转移到状态j一种多少种可能(通过自己匹配自己来预处理p),那么可以看做一个矩阵dp[i-1]和p[][]不断相乘,矩阵快速幂即可
bzoj2120:莫对算法。最基础的莫队,O(1)转移的,按照block[l]为第一关键字,r为第二关键字排序,然后暴力
bzoj2453:主席树/分块。由于数据小,所以也能分块过,有个想法还是很巧妙,pre[i]记录和i同颜色的前面一个的位置,那么对于一个块内就根据pre[]排序;对于修改因为次数不多,所以就暴力寻找前后位置修改。当然也可以用主席树来维护这个pre[i],修改操作就用set来维护
bzoj3343:分块。看似用线段树解决,但实际上并不能用线段树解决。考虑到Q很小,可以分块解决,每次修改,中间块直接打标记,两端块暴力重构,块内排序,询问时二分。
bzoj1444:AC自动机+概率矩阵迭代。题目让求在一个AC自动机上走,最先走到某个单词节点的概率。AC自动机上不是DAG,是有环的,所以处理AC自动机上的概率dp问题可以先构造出初始概率转移矩阵,然后再对这个矩阵自乘不断迭代,重复多次后逼近结果。也可以用高斯消元解决。
bzoj1003:预处理+分段DP。转换思路,不要考虑昨天到今天是否发生路线变化,而去找那个离今天最远的路径不变的那天。考虑最终n天的路线情况肯定就是若干个连续段拼在一块。所以可以预处理出$cost_{i,j}$表示从第i天到第j天都可以走的路径中的最短路径,这里只需要n^2次最短路即可求出,剩下的做分段dp就可以了。
bzoj1002:Matrix定理。基尔霍夫矩阵=度数矩阵-邻接矩阵。去掉基尔霍夫矩阵一行和其对应列,剩下的行列式的值就是生成树个数。
bzoj1192:二进制凑。对于一个数字x,肯定拆成x=2^0+2^1+2^2+...+2^k+p是最优的,但这题有限制,即当p=2^m时候,出现了两个重复的2^m,就不合法了。解决策略是把两个2^m拼起来,一个改成2^m-1,一个改成2^m+1即可。
bzoj3079:悬线+单调栈。做出悬线,然后两次单调栈维护左右两边第一个比该列悬线短的位置。
bzoj1191:匈牙利算法。这题题意有点坑,题意是必须顺着右侧的点来按顺序匹配,若某个点找不到增广路了就break,而不是普通的二分图最大匹配。
bzoj1202:差分约束/并查集。就是给定一些等式s[r]-s[l]=x,其中s是前缀和,判断这些等式是否矛盾。很明显的差分约束。但是因为都是等于关系也可以用并查集来处理,记录当前节点u到根节点的权值和,若当前两个l和r在一个并查集里那么就去check一下他们节点到根节点的权值和的差与x是否一致。
bzoj1084:状压DP。一共有5种状态,但这5种状态如果用位运算来写则十分麻烦,因为状态个数很少只有5个,当状态个数十分有限时,可以直接写出这几种状态的转移式子,这样会更方便。
bzoj1085:双向bfs/迭代加深+A*。很明显的可以双向bfs解决。当然迭代加深的dfs也可以解决,有一个重要的剪枝就是考虑目前状态和目标状态的不相同的格子数,设为x,那么至少还需要x-1步才能跳过去,这样省去了大量的状态。
bzoj1821:最小生成树/二分+并查集。容易想到二分最小距离mid,将距离小于mid的两点连起来,最后看整个图形成了几个连通块,若连通块个数>k,说明这个mid可以继续放大。也可以考虑求整个图的最小生成树的过程,刚开始有n个连通块,每次从小到大加边,每次加一条边减少了一个连通块,所以答案就是第n-k+1条加的边的权值。
bzoj1834:最大流+费用流。先跑出最大流,再对残留网络上加一些可扩容的边,n->T的cap设为k,cost设为0,跑最小费用最大流就行了。注意细节就是第一问跑最大流的边的cost都要设成0,否则跑第二问的时候会形成负环。
bzoj1007:单调栈/半平面交。按斜率排序,维护一个下凸包,若当前直线和top-1直线的交点在当前直线和top直线的交点的右边,则要出栈栈顶。也可以用半平面交做(待补)。
bzoj1832:平衡树。要注意把分数和名字的哈希这对序偶作为权值。
bzoj1040:基环外向树+DP。整个图是个基环外向树森林,先对所有树进行dp,然后对环上的,强制让第一个点取和强制让第一个点不取两种情况,去做两次线性DP。注意此题不能用vector要用前向星存储。
bzoj1818:扫描线+树状数组。显然-1的情况是不存在的。我们把那些横坐标相同的点放一块,当作一个线段,纵坐标相同的点放一块作为一个线段,那么问题就是求线段的交点个数。先离散化,然后扫描线搞一搞就行了,最后记得要减去原本就是线段交点的点的个数。
bzoj3098:生日悖论卡字符串hash。在1~n中随机取,那么取$\sqrt{n}$次出现相同数字的概率大于50%。字符串hash可以看做在0~mod-1内随机映射,所以若字符串的个数多于$\sqrt{mod}$个,那么就危险了,所以mod=1e9+7就很危险的,所以一般都用ull自然溢出,但vfk也给出了ull时候的卡的方法,所以平时还是双hash比较稳妥。双hash要取双mod,双base是没有用的。
bzoj1189:二分+最大流。经典套路了,二分时间,把出口拆成时间个点,连边跑最大流。
bzoj1079:状态压缩。直接状态压缩是不行的,那样状态爆炸。仔细分析我们发现我们关心的只是上一次用的是什么颜色和现在能用哪些颜色,而能用哪些颜色我们可以这样表示:还能使用1,2,3,4,5次的颜料分别有多少种,而上一次用的颜色我们也只需要care它的使用次数是哪个区间的,这样状态表示就可以装的下了。
bzoj1188:sg函数。考虑每个棋子都是独立的,所以整个局面的sg函数就是每个棋子sg函数的异或和,然后考虑每个棋子的sg函数,很明显只和下标有关,直接暴力跑出每个位置的sg函数就行了。
bzoj1013:高斯消元。将1~n个方程与第0个方程相减,平方项就都消除了,只剩一次项了,就可以高斯消元了。
bzoj1305:最大流。二分答案,最大流判定能否形成mid天的舞会。每个男女生拆成3个点,分别是喜欢的点、不喜欢的点和总点,男生喜欢的点连向女生喜欢的点,男生不喜欢的点连向女生不喜欢的点,不喜欢的点和总点之间的容量是k,男生总点和S之间以及女生总监和T之间的容量是mid,判断最大流是否是mid*n即可。
bzoj2064:状态压缩dp。容易发现一定是将初始状态集合的某一子集里的数字和最终状态集合的某一子集里数字相对应,于是dp[S1][S2]表示把集合S1转成集合S2最少需要多少步操作(当然必须要满足sum[S1]==sum[S2]),所以dp[S1][S2]=min(dp[S1'][S2']+dp[S1-S1'][S2-S2']),但这样是(2^n)^4,会超时。仔细分析发现,若初始状态和最终状态有x个互相对应的堆,那么总的方案数就是n+m-2x,所以我们要让x尽可能大。于是dp[S1][S2]表示集合S1和S2最多能对应出多少相等的堆(注意这里就不需要sum[S1]==sum[S2]了,允许有剩余)。这样dp[S1][S2]=max(dp[S1-i][S2]+1,dp[S1][S2-i]+1),即面对一个最优状态,我们能取走仅一个数字使得对应相等的堆个数-1,这样复杂度就是O((2^n)^2)。 WJMZBMR Orz。
bzoj1982:博弈论。棋子不独立,所以没办法用sg函数搞。如果当前局面有偶数堆,且两两个数相同,那么先手必败,因为不管先手怎样做,后手都可以模仿它,使得两堆数目一直相同。其它情况一定先手必胜。先从小到大排个序,比如奇数堆时候,先手可以把最大的那堆去弥补前面两两之间的差值,多出来的直接扔掉;偶数堆的时候,令第二小的和第三小的配对,第四小的和第五小的配对……,最大的去弥补差值,然后将剩余的与第一小的配对,多余的丢掉。
bzoj2178:自适应辛普森。用自适应辛普森求圆面积并。这里的F(a)就是直线x=a被这些圆截的线段总长度,可以每次对所有院遍历求出弦,然后对弦排个序求线段交。
bzoj1412:最小割。这题主要是能看出是最小割,为了把狼和羊割成两个集合。把每个格子看做一个点,S向所有狼连边inf,所有羊向T连边inf,然后每个格子向四周连边,跑最小割就行了。
bzoj3171:循环流。本质是每个点找一个匹配的出点,找个匹配的入点,所以将每个点拆成2个,入点和出点,然后找最优匹配,可以用费用流做。
bzoj2424:dp/费用流。很裸的一个单调队列优化dp(因为上界不动,所以连单调队列都不需要)。然后网上还有很多用费用流的题解……一共有n天,就弄n个点表示第n天的仓库,第i个点向第i+1个点连容量S费用m的边,表示第i天的仓库留多少给第i+1天的仓库,S向每个点连容量inf费用di的边表示订货,每个点向T连容量ui费用0的边表示售出量,跑最小费用最大流。
bzoj1221:费用流。和bzoj2424一样的套路,费用流解决,不过这个要拆点。
bzoj1263:求导+高精度。设将数字x分成n个,那么乘积是(x/n)^n,求个导就发现x/n越接近e越好,那么就是每个数尽可能是3,不能就2。然后压位高精度就行了。
bzoj1232:最小生成树。注意到每条边如果在最终的生成树中,那么它的贡献是2*边权+两个端点的点权,修改边权跑最小生成树即可。
bzoj1691:贪心。第一眼是匹配,但是有100000个点。将牛和草都按照价格排序,价格相同按照鲜嫩程度排序,我们从高往低去看每个牛,对于一个牛,肯定是让他吃掉价格高于他期望的所有草中鲜嫩程度最大的的那个草(因为这样会给后面的牛留有更多机会),于是我们可以倒着将其插入到set里来维护。
bzoj1174:Trie。涉及多个字符串的公共前缀就应该想到Trie。我们求出Trie树每个节点的子树下面单词节点的个数,乘上深度,取个max就是答案。
bzoj2287:dp/cdq分治。第一反应是cdq分治,将dp数组入栈、出栈,时间复杂度O(mnlogn)。也可以dp解决,f(i,j)表示用了前i个物品凑出j背包的方案数,g(i,j)表示丢失第i个物品凑出j背包的方案数,考虑容斥,有g(i,j)=f(n,j)-g(i,j-v[i])。
bzoj1468:点分治。直接点分治,考虑如何计算以某个点u为根的子树内的答案,考虑将其中所有点按照到u的距离排序,然后two point从两端移动就能计算出答案,但这个答案有的是过u的,有的是不过u的,所以那些不过u的我们要减去。
bzoj2127:最小割。很经典的最小割,考虑两个有关系的点u,v,S表示文科集合,T表示理科集合,S->u S->v u->T v->T u->v v->u 这些边描述u和v的联合权值,枚举所有割的可能情况,列方程,求出这些边上该赋的权值即可,最后不要忘了S->x x->T连上他本身的权值。
bzoj2132:最小割。类似bzoj2127这样画图列方程,但发现有负权。我们可以黑白染色,S->黑色格子是商业区,而S->白色格子是工业区,这样做的好处就是如果两个点u,v不在一个割集里,那么就相当于在实际意义上他们是一个商业区,这样损失的值就是正值了,没有负权了。即我们希望遇见的相邻格子颜色相同时候有加分,而不是相邻格子不同时有加分,如果遇见后者就黑白染色。
bzoj1635:构造。对于一组约数(a,b),我肯定是希望a和b一样高,a+1~b-1都比a低一个高度,于是可以用差分来表示一下,最后扫一遍结果就出来。
bzoj1641:floyd。f[i][j]=min(f[i][j],max(f[i][k],f[k][j]))
bzoj3170:切比雪夫距离转曼哈顿距离。(x,y)->(x+y,x-y)就是曼哈顿距离转切比雪夫距离,反过来就将切比雪夫距离转成了曼哈顿距离,然后曼哈顿距离情况下将x和y独立处理,分别排下序统计下每个点作为终点的答案,最终取个min就行了。
bzoj1391:最小割。如果没有每台机器的租用,全部是购买花费的话,那么就是最大权闭合子图了,但现在多了一个租用费。注意到最大权闭合子图里我们中间的边都是inf,这代表着如果选定了一个u,那么下面的v都要选,现在我们可以把inf改成对应的租用费,就是说可以不选择v,但要付出些代价,对这个图跑最小割即可。
bzoj2957:线段树维护单调栈。线段树维护单调栈,主要是maintain的时候合并两个单调栈就行了,显然我们不能把单调栈里的元素都存进来,我们存单调栈最右边的元素(即区间最大值)和单调栈里元素个数,那么就可以处理合并了,将[l,mid] [mid+1,r]合并主要是要找以mx(l..mid)进入右边单调栈是多少,这个只需要每次向下找就行了,时间复杂度是O(nlog^2n)。
bzoj1874:sg函数。暴力跑出每堆的sg函数,再把所有堆异或起来。
bzoj1863:二分+dp。先二分答案,然后判定mid种颜色是否可行,我们希望求出最后一个是否可以和第一个不冲突,设mn[i]表示满足前i个要求,第i个和第1个的最少重复颜色,我们发现要让i和1重复最少,需要让i-1和1重复最多,所以还要在设出一个mx[i]表示最多重复颜色,然后顺序dp就行了,判断mn[n]是否为0。
bzoj1483:链表合并。每次将两个链表启发式合并。
bzoj3158:最小割。列出两个相关点的方程组,发现有负边权,于是老套路考虑黑白染色(并且也正好可以黑白染色,因为奇数-奇数 偶数-偶数 不会同时具有题目中的两条性质),考虑黑白染色之后我们发现只需要奇数->偶数连一个inf的边就行了。
bzoj2396:随机算法。随机一个n行1列的矩阵D,比较(D*A)*B和D*C是否相同。
bzoj3450:概率dp。f[i]表示前i个位置以第i个位置为结尾的o的个数的期望,g[i]表示前i个位置的期望得分,O(n)去递推一遍就行了。
bzoj1977:严格次小生成树。如果要求次小生成树,那就是跑出最小生成树然后枚举树外边(u,v),在树上查询(u,v)上的最大值即可,现在要求严格最小,也就是说当树上(u,v)的最大值正好等于我们的树外边权值时候,我们要去删权值次小的(u,v)上的边,所以预处理和树上倍增的时候我们只需要记录最大值和次大值即可(次大值是权值次大)。
bzoj2879:费用流。很明显将厨师拆点,但问题是此题规模很大,如果都拆点会TLE。这里可以采用动态加边,比如当前如果第i个厨师的第j个点被纳入最大流了,再把他的第j+1个点对应的图加入进去。
bzoj1853:容斥。预处理出所有幸运数字然后去容斥,注意到虽然幸运数字有几千个,但是其中几个数的lcm就已经超过10^10了,所以暴力dfs去统计非0答案是可行的,注意运算过程中会爆longlong,所以要取log判断位数是否超过才能相乘。
bzoj2120:主席树/带修改莫队。裸的树状数组套主席树就不说了,这题因为数据范围很小,所以也可以用带修改的莫队来做,带修改的莫队主要思想就是在排序询问的基础上对修改也按照时间排序,用一个time指针来处理修改对当前询问的影响,时间复杂度是$O(n^{3/5})$的。注意block应该是$n^{2/3}$
bzoj1854:并查集维护基环外向树。对于左边点连向的两个点,我们把其中间连上一条边,最终任务就是使每条边定向使得尽可能每个点的入度都是1(某些边可以不要)。我们去单独考虑每个连通块,如果这个连通块是个树,那么很显然有一个点不能被安排,我们贪心的让编号最大的那个点不能被安排;若连通块是个基环外向树,那么所有点都能安排。所以我们读入每条边,用并查集去维护基环外向树,最后再统计可以被安排的最大编号的点就行了。
bzoj3280:费用流。仍旧老套路,将每一天的任务建成一排点,“救治病人”需要i天就相当于将第j个点指向第j+i个点一条出边,其它的对应建建图就行了。
bzoj3293:中位数。如果是线性的话,令s[i]表示第i+1个人要给第i个人多少金币,那么有$S_i=S_{i-1}+average-a_i$,直接累加就行了,现在是环形,就是第一个和第n个直接有直接的金币供应,设第n个人给第1个人k个金币,那么每个$S_i$都减少k,所以k就是所有$S_i$的中位数时候结果最小。
bzoj2697:贪心。容易发现同样一种动作做多次和做一次是一样的,所以按照从大到小从两边往中间放。
bzoj1584:DP。第一眼是决策单调性,但很遗憾一段的代价不能用式子表示出来。有这样一个性质,如果[j,i]中不同颜色的个数k>sqrt(n),那么我们可以不用往前再枚举j了,因为如果我们令这i个数独自为块,则代价是i,显然比k^2小,但这样我们只是能够减少我们常数但不能将转移复杂度降到sqrt(n),容易发现在sqrt(n)种k中,在连续一段的最左边的是我们的待转移决策,即我们的转移决策只有sqrt(n)个,所以我们只需要动态去维护p[i][k]表示从i往前移动,出现次数为k的最左端的坐标。从p[i-1][]推出p[i][]的过程也很简单,我们只需要判断分界点左边的数和a[i]是否相等即可。
bzoj2555:lct+sam。lct维护sam的slink树。