感觉自己真的好菜啊,要继续学新算法了qwq,然后因为要二检时间并不是很多了。
BZOJ1101——Math——Mobius
LGP3644——Data_Structure——Balanced_Binary_Tree——Splay
LGP3645——Map——Shortest_Way——SPFA
BZOJ3206——Map——MST
BZOJ4585——Others——Convex_Hull
LGP3622——DP——Compression
BZOJ3676——String——manacher+DA
BZOJ4804——Math——Phi
BZOJ3064——Data_Structure——Segment_Tree
LGP3629——Others——Miscellaneous
BZOJ4530——Data_Structure——LCT
BZOJ4561——Data_Structure——Balanced_Binary_Tree——Splay
CF632E——Math——FFT
LGP2570——Map——Network_Flow——Cost_Flow——Common
LGP3647——DP——Tree
POJ2069——Others——Simulated_Annealing
UOJ26——Others——Interactive
LGP3401——Data_Structure——Heavy_Light_Cut+Segment_Tree
BZOJ4919——DP——Data_Structure——Segment_Tree
BZOJ4066——Data_Structure——KD_Tree
以上都没有贴题解qwq,比较好的一些题会放在博客里面的。
比较有空就贴上题解。
BZOJ3489——Data_Structure——KD_Tree
KDT的正确姿势,在传统数据结构题中的妙用。
我们记录每一个点为一个三元组(x,pre,nex)表示当前点位置在x,前一个和它相同的数位置在pre,后一个在nex。
那么对于每个询问我们要找的就是l<=x<=r,pre
BZOJ2565——String——PAM
求最长双回文串嘛,上PAM。
正串和反串各做一次PAM,记录答案即可。
CC_CUTTREE——Math——FFT/Divide_Conquer——Point_Divide
题解
URAL2040——String——PAM
PAM模板题
接下来就没有机会去机房了,要准备二检。(班主任暴怒了)
然后这两周文化课的间隙就看一些论文好了,学习一些新姿势qwq。
不要问我为什么写了题。
CC_PRIMEDST——Math——FFT/Divide_Conquer——Point_Divide
模板题不解释了。
HDU5785——String——PAM
首先如果我们求不重叠回文子串的对数那么回文树中,num[now]表示添加该字符后新产生的回文串个数,即以该字符为结尾的回文串个数,num[now] = num[fail[now]]+1
正着跑一次,同时求出num的前缀和,再反着跑一次,同时累加prefixSum[i-1]*suffixSum[i]即可
这道题中则要求以该字符为结尾的回文串的起始坐标和,不方便直接维护和递推
后来想到,sum[now]表示以该字符为结尾的回文串长度和,则sum[now] = sum[fail[now]] + len[now]
则 (pos[now]+1)*num[now] - sum[now] 即是起始坐标和,反着跑时也可以类似的求出终止坐标和,相乘累加即可
总是在不可名状的地方爆了long long
没什么时间更新博客了,各种课程十分乱,文化课真难qwq。
BZOJ1898——DP——Matrix
算出每12步的矩阵,做矩阵乘法。其余的再单独暴力。
BZOJ3270——Math——Gauss
列出方程高斯消元即可。
CF975E——Calc_Geometry——Barycentre
发现每次旋转的特性是和重心有关,且最后重心与固定点的直线垂直地面。
这样我们只需要维护重心的位置以及到每一个角的大小就可以了(事实上角的大小是预处理)
CF986A——Map——BFS
对于每种颜色跑一次bfs。
CF983D——Others——Scan_Lines
题解
CF986D——Math——FFT
经过一番推敲,发现答案一定是尽量多选三,最后会有 2∗3x,3∗3x,4∗3x 2 ∗ 3 x , 3 ∗ 3 x , 4 ∗ 3 x 三种情况。
怎么求出这个 x x ?用换底公式,其实就变成了 x<=log3(n)=len(n)∗log3(10) x <= l o g 3 ( n ) = l e n ( n ) ∗ l o g 3 ( 10 ) ,后面这个东西按计算器就行了233,当然c++自带也可以。
之后的计算就是一个FFT+快速幂。
BZOJ4832——Math——Probability&Expectation
这个题目顺着做很好想,设
f(i,a,b,c) 表示 攻击了i次 奴隶主中血量为1的有a个 血量为2的有b个 血量为3的有c个 英雄所受伤害的 期望
g(i,a,b,c) 表示 达到这个状态的 概率
然后随便转移一下就可以了。
上面方法使用的是期望=概率*权值,因此我们需要维护概率。
但是这类问题还又一种写起来很简短的做法,设
f(i,a,b,c)表示攻击还剩i次…的期望,相当于把整幅图反了过来,变成了一幅拓扑图进行递推,就只用维护期望了。
其实这种方法在数学期望里是经常用到的,即将“已经x次”变成“还剩x次”。
BZOJ1040——Map——Circle_Tree
环套树练习。
如果只是一棵树,我们只需以其中一个点为根进行简单dp即可。
现在是一棵环套树,我们可以将环上的其中一条边断掉,对这条边连接的两个点分别作一次不选的dp取最大值即可。
CF455E——DP——Slope
题意很简短, f[1,j]=a[j],f[i,j]=min(f[i−1,j],f[i−1,j−1])+a[j] f [ 1 , j ] = a [ j ] , f [ i , j ] = m i n ( f [ i − 1 , j ] , f [ i − 1 , j − 1 ] ) + a [ j ] ,多个询问求 f[x,y] f [ x , y ]
很容易发现答案一定是一段横着走加一段斜着走,考虑 f[1,i] f [ 1 , i ] 对 f[x,y] f [ x , y ] 的贡献,那么就是:
(x−(y−i))∗a[i]+sum[y]−sum[i] ( x − ( y − i ) ) ∗ a [ i ] + s u m [ y ] − s u m [ i ] ,其中 sum[i]=∑ij=1a[j] s u m [ i ] = ∑ j = 1 i a [ j ]
然后按照斜率优化搞一搞,得到 i i 比 j j 优时:
(x−y)∗a[i]+i∗a[i]−sum[i]<(x−y)∗a[j]+j∗a[j]−sum[j] ( x − y ) ∗ a [ i ] + i ∗ a [ i ] − s u m [ i ] < ( x − y ) ∗ a [ j ] + j ∗ a [ j ] − s u m [ j ]
这样每个点就是 (a[i],−sum[i]+a[i]∗i) ( a [ i ] , − s u m [ i ] + a [ i ] ∗ i )
求最小值的话我们维护一个下凸壳就行了,在凸包上二分即可。
CF689C——Math——Probability&Expectation
10^100可以近似看作INF了,这样到最后k个缓存一定都是放满的(如果有0的概率特判来做什么的就行了)
这样将问题倒过来,转化为如果第i个视频没有放进去,那么以概率pi放进缓存,当缓存中视频数为k时停止,问每个视频出现概率。
n很小可以考虑状压,对于当前状态now,若 now n o w & (1<<i)==0则f[now|(1<<i)]+=f[now]∗p[i]/tot ( 1 << i ) == 0 则 f [ n o w | ( 1 << i ) ] + = f [ n o w ] ∗ p [ i ] / t o t ,其中tot表示没有出现在now中视频p的和。
最后的话就统计一下1的个数为k的二进制概率累加答案什么的。
LOJ516——Data_Structure——Balanced_Binary_Tree——Set
启发式合并set
BZOJ3672——DP——Slope
题解
BZOJ3242——Map——Circle_Tree
考虑答案的性质,如果这幅图是一棵树,那么答案显然就是树的直径。
现在是一棵基环树,如果答案不经过环,那么答案就是所有外向树的直径的最大值。
考虑经过环的答案,路径一定是形如x_son->x->y->y_son的,因此我们可以尝试去掉环上的一条边,这样就变成了一个区间问题。
如果我们枚举去掉的是环上的哪条边,可以很容易得到答案。
但现在问题在于,依次去掉环上的边,如何快速求出直径?
假设现在有一个4的环,我们写成1-2-3-4-1-2-3-4的套路形式。
设dis_i为节点i到链起点点的长度,,dep_i表示树深度那么答案就是max{dis_i-dis_j+dep_i+dep_j}(j
BZOJ4814——Others——Segment_Division
看到序列上的拆入删除操作,想起APIO的线段树分治。(事实上也确实是为了练这个)
为了避免删除操作,我们记录每个点的加入和删除时间。
线段树分治实际上就是相当于在每一个节点打一个标记,表示这个节点表示的时间内有哪些操作
最后我们只需要dfs一遍线段树就行了。
LOJ2585——Data_Structure——Segment_Tree&Splay/Segment_Division
题解
BZOJ1095——Divide_Conquer——Dynamic_Divide
先考虑没有开关灯的操作,我们可以直接用一个点分治来完成。
现在有开关灯,考虑利用点分治的性质完成。
我们将分治过程中分治的一层层重心看作是一棵新的树,每个分治重心向它的上次分治重心连边,我们就得到一棵深度严格log的点分树。
考虑通过维护点分树上的信息来解决问题,我们发现每次修改影响的只是点到根的路径。
我们维护三个堆:
A:每个重心存储所有子树中节点到它的距离。
B:每个重心存储每个子树的最大距离,即所有子节点的A的最大值建成堆B
ans:维护答案最大值,即每个堆B的最大值和次大值之和。
这样就可解决这道题了。
BZOJ2959——Data_Structure——LCT/DSU
如果没有加边操作,可以直接tarjan+树剖来做。
现在有加边,实际上我们要维护的就是动态缩点和动态求链值。可以考虑用LCT来维护。
如果加入某条边之前两个端点就联通,我们就将这两个点之间的路径提出来,缩成一个节点,并用一个并查集维护每个点所属的元素代表信息。
然后就是LCT的操作了。注意缩环以后的fa要在DSU上find。
LGP3936——Others——Simulated_Annealing
模拟退火好题,换了几个种子才过。
BZOJ3572——Others——Virtual_Tree/DP
虚树入门题,那么虚树是什么呢。
对于一类树上问题,如果询问多但是涉及总点数比较少的话,我们可以去掉那些无关的点。
能够保留的点就是所有有关的点和他们的LCA。
虚树的构建:我们先再次把所有点按dfs序排序,然后我们考虑这样一种方法,我们维护一个栈,如果栈为空,直接加点,否则我们看栈顶的点是不是当前点的祖先,如果不是,那么弹出栈顶继续,否则就可以把当前点加进栈了(然后从栈顶向这个点连一条边)。(因为之前插入的点可能是和当前点是并列关系,这个不一定会需要用到lca,因为我们记录一个dfs时入队和出队的顺序就可以了)。(第一个点就直接作为根了)。
对于这题,构建虚树后我们分别从叶子往根和从根往叶子dp,处理出虚树上距离每个点最近的议事处。
接着枚举虚树上每一条边,考虑它对两端点的答案贡献,如果两端点不属于同一个点,显然会存在中间分界,我们倍增找出分界点后对两边计算贡献即可。
注意还要另开一个g数组记录没有在上面讨论到的节点,这些节点会与他们在虚树上的父亲属于同一节点,我们要删去多算的部分。
LGP4714——Math——Miller_Rabin&Pollard_Rho
分解质因数以后就乘一乘计算贡献:如果第 i i 个质因子有 pi p i 个,一共有 n n 个质因子,那么答案就是:
大概吧,随便yy一下应该也可以了。
BZOJ5251——Map——Network_Flow——Max_Flow——Common
对于第一个问题,如果我们并不需要知道每个人匹配到了谁,实际上就是一个二分图匹配。
现在要求知道每个人匹配到谁,我们只需要动态加边跑网络流,即对当前考虑的选手向它的导师连边,看增广了哪条边。
对于第二个问题,二分暴力重构图就可以过了。更优秀的方法是先连上所有这个点可以连的边,然后从第一名开始增广,直到找不到增广。
这样两个的复杂度都是 O(n∗n2) O ( n ∗ n 2 ) ,其中 n2 n 2 是二分图匹配的复杂度。整道题的复杂度就是 O(T∗n3) O ( T ∗ n 3 )
BZOJ4372——Divide_Conquer——Dynamic_Tree_Divide
又一道动态点分治。
我们对点分树上的每个点开一棵线段树,维护子树到这个点距离为x的所有增量。
如果直接这样统计的话显然会算重,一种简单的处理方法是再开一棵线段树维护子树到它父亲距离为x的所有增量。
事实上开的这个线段树是多余的……不过思想就是这样了。
查询修改都是往上算/改到根就行了。
嗯,支持区间修改单点查询的简单线段树。
BZOJ3648——Divide_Conquer——Point_Divide/Map——Circle_Tree
BZOJ4018——Math——Mobius
CF623E——Math——FFT
YALI_Walk——Others——Miscellaneous/Math——Miller_Rabin&Pollard_Rho
题解
OTHER_1——DP——Tree
CF986E——Others——Virtual_Tree/BIT
每个素数是独立的,一个数最多拆成 log l o g 个不同的素数,这样分解出的所有素数个数为 nlog(maxval) n l o g ( m a x v a l )
考虑对每个素数建立虚树,这样虚树的总点数和素数个数同阶。
对于每颗虚树处理出答案,我们关注的是每个点这个素数的个数,用倍增+BIT维护。
再利用处理出来的虚树贡献答案即可。
BZOJ5392——Others——Scan_Lines
题解
BZOJ2286——Others——Virtual_Tree
每次按照关键点的dfs序排序,维护栈构建一棵虚树,在新的树上dp
BZOJ4044——String——PAM
考虑最后一次2操作,那之后的字符都是暴力拼上去的。那么可以枚举每一个回文子串,算出拼出它的最少次数,然后加上总长度减它的长度来更新答案。
那么考虑怎么算出每一个回文子串的最少次数。
对于一个长度为偶数的回文串:可以用反证法证明最后一步一定是2操作。那么分两种情况:2操作前向外添加字符,可由这个串去掉两端+1得到。2操作前向内添加字符,可由最长的小于等于一半长度的回文后缀+(一半长度-该后缀长度)得到。既向内又向外可归类到只向外中。
对于长度为奇数的回文串:去掉两端+2,或最长的小于等于一半长度的回文后缀+(整个长度-该后缀长度)。
如何快速找到小于等于一半长度的最长回文后缀?它在回文树上的父亲已经找过了,在此基础上接着找即可。显然最多只可能有一次长度不符合要求,所以不影响复杂度。