Educational Codeforces Round 12 F
大意:
求n(n<=1011)以内恰好有4个因数的数的个数
分析:
首先一个数恰好有
设
这一点我们用dp来解决。
记pj为第j个素数,
1.dp[n][1]=n
2.dp[n][j]=dp[n][j+1],dp[⌊n/pj⌋][j],
也就是说dp[n][j+1]=dp[n][j]−dp[⌊n/pj⌋][j]
令pk是最小的大于n−√的素数,那么π(n)=dp[n][k]+k−1
Codeforces Round #346 G
大意:
给n(
分析:
记dp[l][r]为取走的区间为[l,r]的方案数,总方案数就是
l=r时,dp[l][l]=hl−1
l<r时,dp[l][r]=min(hl−1,hl+1−1)∗Πr−1i=l+1min(hi−1−1,hi−1,hi+1−1)∗min(hr−1−1,hr−1)
那么我们有
ans=∑ni=1(hi−1)+∑nl=1∑nr=l+1min(hl−1,hl+1−1)∗Πr−1i=l+1min(hi−1−1,hi−1,hi+1−1)∗min(hr−1−1,hr−1)
关键在于后一个长长的式子,即
∑nl=1∑nr=l+1min(hl−1,hl+1−1)∗Πr−1i=l+1min(hi−1−1,hi−1,hi+1−1)∗min(hr−1−1,hr−1)
=∑nr=2min(hr−1−1,hr−1)∗∑r−1l=1min(hl−1,hl+1−1)∗Πr−1i=l+1min(hi−1−1,hi−1,hi+1−1)
令S(r)=∑r−1l=1min(hl−1,hl+1−1)∗Πr−1i=l+1min(hi−1−1,hi−1,hi+1−1)
则S(r+1)=S(r)∗min(hr−1−1,hr−1,hr+1−1)+min(hr−1,hr+1)−1
然后就可以线性做了
Codeforces Beta Round #2 B
大意:
给一个n∗n的矩阵,每个格子有一个数,从左上到右下,只许向右或向下,求一条乘积末尾0最少的路径
分析:
考虑每个数质因数分解,末尾0只会来自于2*5,因此我们只要最小化路径上2和5的数量,不难发现2和5分别独立,最优解必然选择2和5中的更优解,问题转化为包含2最少的路径,记状态为dp[i][j],直接转移即可
Codeforces Beta Round #5 C
大意:
给一个括号序列,求一个最长合法括号子串
分析:
(分类是dp不知道为什么)用一个栈搞一搞就行了。碰到左括号就入栈,右括号就弹栈顶,弹的时候更新答案
Educational Codeforces Round 12 C
大意:
给一个小写字母串s(
分析:
(分类是dp不知道为什么)显然对于一段相同的,假如说有k个,我们最少只需要
Educational Codeforces Round 11 C
大意:
给一个长度为n(n≤3∗105)的01序列,允许k次将某一个
分析:
(分类又是dp不知道为什么)注意到我们操作的本质是选一段区间里面0的数量不超过
Codeforces Beta Round #8 C
大意:
给出n(n<=24)个物品的坐标和箱子的坐标,人从箱子开始,每次最多运两个东西,必须把东西放回箱子,最后在箱子结束,求最小总移动距离。
分析:
n很小,我们直接状压,dp[S]表示目前还没放回的物品的状态集合,并且此时人在箱子的最小移动距离,n2转移,看起来会T,实际上不满。
CROC 2016 - Final Round C
大意:
给一个n行
分析:
显然我们最多只会操作
首先我们有一个O(2n∗m)的算法,我们枚举每一行是否翻,然后再一列一列考虑,如果能使1更少就翻。
我们记coli表示第i行一开始的状态,
∑mi=1min(popcount(maskxorcoli),n−popcount(maskxorcol[i]))
注意到我们不关心每一行异或的值具体是多少,我们只关心里面1的个数,因此我们记
现在的问题是如何计算dp[k][mask]考虑一个coli与mask在第p位不同,那么
BZOJ 2004 [Hnoi2010]Bus 公交线路
大意:
要求给
分析:
像这样间隔不超过一个很小的数的条件,往往启发我们对前面若干位进行状态压缩。
在本题中,对于每个格子,我们只关心它前P个格子的染色状况,因此我们考虑对前
记录各种颜色已经从左往右染到了哪里。
如果就这样直接转移,很快我们会发现这样会导致重复计数,并且有些格子可能根本没有被染色,因此我们需要加强对转移和状态的限制,我们限制状态的最高位必须为1,转移时必须从这个
BZOJ2073 [POI2004]PRZ
大意:
将一群人分为若干组过桥,一组人的总重不能超过一个数,一组人过桥耳朵时间是这组人里面最慢的那个,最小化所有人过桥时间,人数≤16
分析:
人数很小,我们对每个人是否已经被分配组状态压缩,对于一个状态,我们需要枚举它的一个子集进行转移。
枚举子集可以这样方便的实现
for(int j = i; j; j = i & (j - 1))
其中i是全集,
可以证明枚举子集的复杂度是O(3n)
Codeforces Beta Round #8 E
大意:
考虑长度为n(n<=50)的所有01串,两个01串本质相同当且仅当它们可以通过01互变或串反转得到对方,求字典序第k(k<=1016)小的与小于它的所有串本质不同的串。
分析:
对于这种求字典序K小的合法串的问题,我们一般考虑逐位确定。
显然和一个01串本质相同的串最多只有3个,分别是串反转,01反转,串反转+01反转,而我们的答案要求的就是第k个小于和它本质相同的三个串的01串。
假设当前已经确定的前缀为
记dp[l][f1][f2]表示当前为第l位,是否小于串反转,是否小于01反转,枚举第l和n+1−l位是0/1进行转移,记忆化搜索实现。
BZOJ 4521 [Cqoi2016]手机号码
大意:
求[L,R]以内至少出现了一次相邻的相同的三个数字,不同时出现8和4的11位数的数量
分析:
典型的数位dp,状态dp[dep][last][cnt][cons][eight][four][lim]
表示第dep位 上一个数是last 上一个数连续了cnt次 是否已经有过相邻的三个相同数字,是否出现过8,是否出现过
BZOJ 3209 花神的数论题
大意:
令Sum(i)为i在二进制下
分析:
我们变换一下思路,转而求[1,n]以内二进制位恰好有k个
那么这个问题就可以数位dp了
Codeforces Beta Round #9 D
大意:
n个点的深度不低于h的不同的二叉搜索树的数量(n<=35) 键值为[1,n]
分析:
首先我们将深度不低于h转化为深度恰好h,记dp[n][h]为n个点深度恰好为h的二叉搜索树的数量。
显然dp[0][0]=1,否则dp[i][0]=dp[0][i]=0
考虑任意一棵n个点深度为h的 二叉搜索树,假如根上的数是m,则左子树有
1.左子树深度恰好为h−1,左子树的合法数量为dp[m−1][h−1],右子树深度随意,为∑h−1h′=0dp[n−m][h′],乘法原理即可得到这种情况的合法数量
2.右子树深度恰好为h−1,这与情况1其实差不多,不多赘述。
然后我们就可以求出dp[n][h]了
BZOJ 1040 ZJOI2008 骑士
大意:
求一个环套树的最大点权独立集
分析:
一般来说,环套树上的问题我们总考虑选取一条边然后破环转化为一般树的问题。
记f[i]为以i为根的子树在取i点的情况下的最大权值,g[i]为不取
取环上任意一边破环,设两边为u,v
于是有两种情况
u不取,v任意,我们以u为根跑一遍树形dp,取g[u]
v不取,u任意,我们以v为根跑一遍树形dp,取g[v]
Tips: 一般树最大点权独立集
记f[i]为以i为根的子树在取i点的情况下的最大权值,g[i]为不取
我们有
BZOJ 3037 创世纪
大意:
求一个环套树的最小支配集
分析:
一般来说,环套树上的问题我们总考虑选取一条边然后破环转化为一般树的问题。
设f[x]为以x为根的子树选择x的最小支配集,g[x]为不选择x的最小支配集
取环上任意一边破环,设这个点为x 出边指向的点为
讨论
1.若x选择 则y一开始就是被支配状态 g[y]初值为0 求一遍最小支配集
2.若
Tips: 一般树最小支配集
和最大点权独立集状态类似只不过有三种状态,一是i在支配集中,二是
BZOJ 4033 HAOI2015 T1
大意:
给定一棵树,你需要把其中的
分析:
用f[i][j] 表示在以i 为根的子树,有
这个权值指的是,这个子树内部的点对间距离的贡献,以及i和fa[i]之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值)。
然后DFS来求,枚举i的每个儿子j,现在的f[i][]是包含了[1,j−1]子树,然后两重循环枚举范围是[1,j−1]的子树总Size和j的 Size,来更新f[i][],这样更新之后的f[i][]就是[1,j] 子树的答案了。
BZOJ 1131 POI2008 Sta
大意:
给定一个n个点的无根树,要求找到一个根节点,使深度之和最大
分析:
对于这种无根树换根的问题,我们一般考虑做两遍dp,第一遍任意选择一个点为根dp出子树信息,从儿子推父亲,第二遍dp出某一个点为根时其它子树的信息,从父亲推儿子
在本题中,我们先把这棵树处理成以1为根的有根树,维护以每个点为根的子树的节点数
Codeforces Beta Round #14 E
大意:
给一棵n(n<=200)个点的树,求两条不相交路径使得路径总长最大
分析:
观察发现两条不相交路径必然可以通过割断树上的某条边之后分别处于割出来的两个子树里,因此我们枚举割断某条边,dp出两侧的最长路,这种问题我们也需要两遍dp,一遍父亲推儿子,一遍儿子推父亲
BZOJ 2286 SDOI2011 消耗战
大意:
给定一棵树,边上有边权,m次询问,每次选定一些关键点,求将
分析:
首先我们需要用到一种新的武器——虚树
虚树是这样一棵树:在原树上选出若干关键点,将没有分叉的边全部压缩成一条边,从而得到一棵与关键点数量成线性的树
对于像该题的问题来说,本来我们应该对于每个询问都对整棵树重新做一边树形dp,但这样显然太费时。
对于每个询问,我们如果能够建出它对应的虚树,那么在虚树上dp的总复杂度就只与总询问点数有关,大大降低了时间复杂度
如何维护虚树呢?我们这样做
维护一个栈,栈中的元素形成一条由根节点出发的链,初始栈中只有根节点
将所有关键点按照DFS序排序
每次加入一个节点,求出节点与栈顶的LCA,将栈中所有深度大于LCA的节点全都弹掉
然后将LCA和该节点入栈,注意有些重复的情况要考虑
知道如何维护虚树之后,我们继续考虑dp的问题
令f[x]表示切断以x为根的子树中所有关键点的最小花销
那么对于
然后我们直接在虚树上dp就可以了。
BZOJ 3162 独钓寒江雪
大意:
给定一棵树,求本质不同的独立集个数对1000000007取模后的值
分析:
首先这里涉及到一个问题——本质不同
因此首要要解决的问题是无根树同构
我们给出如下基本知识:
1.直径是树上最远点对之间的路径。
2.取直径的中点称为中心。直径长度为偶数时,中心在结点上;直径长度为奇数时,中心在边上。
3.树上的所有直径都过中心。
4.找一条直径的方法是从任意一个点 bfs 到树上最远点 x,然后从
5.无论标号如何,树的中心位置是确定的。
通过寻找重心我们可以将无根树同构转化为有根树同构,判断有根树同构最基本的方式是最小表示法
定义S[v] 表示以v为根的子树的括号序列。
S[v]=‘‘(”+S[c1]+S[c2]+⋅⋅⋅+S[ck]+‘‘)”
我们规定了子树的顺序(即按字典序的顺序),所以同构的树的括号序列表示具有唯一性。
而我们发现直接使用括号序列太慢了,所以我们可以使用字符串哈希
然后我们就可以判断树的同构问题了。
考虑一个点i,首先我们将同构的儿子并在一起,用组合数计算出它们之间本质不同的方案数,然后对于不同构的儿子直接乘法原理即可