CF practice #1 solution

来源:互联网 发布:恩牛网络 编辑:程序博客网 时间:2024/06/04 19:50

CF practice #1solution

题目链接

A:cf 144 div

题意:给出一种图的构造方式,d(1)和d(2)已知,d(n)的构造方式为把d(n-2)的所有节点编号加上|d(n-1)|,然后把d(n-2)的最小节点和d(n-1)的最大最小节点都连一条边,这样构造成了d(n)。然后给一个n,问你在d(n)中,两个节点a,b的最短路长度是多少。

数据范围:n<=1000 , q(询问数量)<=105,a,b<=1016

 

Solution:首先a,b是小于1016的,那么由此可知n不会超过80,否则和n=80没区别,然后分开考虑下面几种情况:

当前图为d(n),假设a<b:

1.      a,b均在d(n-2)中:

solve(n,a,b)=solve(n-2,a-|d(n-1)|,b-|d(n-1)|)

2.      a在d(n-1)中,b在d(n-2)中:

solve(n,a,b)= min( solve( n - 1 , 1 , a ) , solve( n - 1 ,a , f[n-1] ) ) + solve( n - 2 , 1 , b - f[n-1] )+1

3.      a在d(n-1)中,b在d(n-1)中:

min(solve( n - 1 , a , b ) , min( solve( n - 1 , 1 , a ) + solve( n - 1 , b ,f[n-1] ) , 

                             solve( n - 1 , 1 , b ) + solve( n - 1, a , f[n-1] ) ) + 2 ) ;

因为只有a,b两个点,所以化到每个n的状态最多产生4个点1,a’,b’,|d(n)|。所以对每个n的每种状态可以用记忆化的方式避免重复计算,这样的话状态总数只有n*4,即大概360个状态,还有一个优化就是如果solve(n,a,b)的a=1,b=|d(n)|时,答案为(n+1)/2。这样的话只要调用solve(n,a,b)就能得到答案了。

B:cf 143 div2 C

题意:给n个数的一个序列,然后给一个操作次数k,每次操作可以选择一个数加1,问最后出现次数最多的数会出现多少次,这个数最小是多少。

数据范围:1<=n<=105,0<=k<=109

 

Solution:把所有的数按照大小排序,有一个性质可以发现就是说某个数出现了x次的话,那么它一定是在当前序列中连续出现x次,那么可以二分出现的次数,然后判断该次数下是否有解。

判断的方法很简单,首先把前x个数拿出来,把这x个数的值全部加到第x个数的大小,如果使用次数小于k,那么就直接有解了,否则移动这k个数的框框,每次把框框中的数全部变为最后一个数,如果在某k个数中消耗小于k,那么有解,如果均不满足,则无解,框的移动可以用O(1)的时间来维护,一次判断的时间为O(n),总复杂度为O(n*log(n))。

 

C:cf 143 div2 D

题意:空间上给了一个以(0,0,0)为一个顶点的长方体,长方体每个面都平行于坐标面,每个面都有个数字,给一个长方体之外的坐标,问从这个坐标看过去看到的数字的和是多少。

数据范围:关系不大,略。

 

Solution:首先考虑一个面,每个面都有内侧和外侧,数字是写在外侧上的,如果从一个点能看到这个面,那么这个点必然在平面的外侧的那一边。那么做法就很显然了,枚举6个面,然后依次判断这个点是不是在这个面的外侧面那一边,是的话就把答案加上这个面的数,否则不加,最后输出答案。

 

D:cf 143 div2 E

题意:给一个n个点,m条边的无向图,保证每个顶点最多只属于一个简单环,给出q个询问,每个询问给出a,b问从a到b在每条边最多走一次的情况下有多少种不同的方案。

数据范围:n<=105,m<=105,q<=105

 

Solution:每个顶点最多只属于一个简单环,那么就表示每个环之间都没有交点,我们可以把所有的大于2个点的边双连通分量缩点,那么原图会成为一个森林,把缩得的点标记为黑点,其余点标记为白点,那么两个点在树上的路径中,假设有x个黑点,那么简单路径的数量必然等于2x,如果两个点属于2棵不同的树,那么答案为0。

至于如何找两点之间的黑点数量,就是一个标准的lca问题了,对于两点是否在一个子树内,可以做一个虚拟的根0,连接每棵树的树根,最后如果询问两点的最近公共祖先是0的话,就表示不在一颗树内了。

复杂度为边双连通分量缩点O(n)加上lca的复杂度O(q*log(n)),总复杂度O(n+q*log(n))。

 

关于该题的几种变形:

1.      如果每个点只能经过一次。

显然路径的总数与给出的点是否是关节点有关,上述方法不再有用,一个好的方法就是把原图按照点双连通分量缩点,关节点和普通点为白点,大于2个点的点双连通分量为黑点,其他照旧。

2.      如果每个点可以属于多个简单环,但环之间最多共1个点。

(1)    如果每个点只能走一次

方法应该和1是一样的。

(2)    如果每条边只能走一次

不知道如何做,求解。

3.      给一个无向图,问任意两点间的简单路径条数。

不知道有没有好做法,应该是np-hard吧。

 

E:cf 142 div1 A

题意:给出一个n*m的矩阵,定义一步操作为把某一行向左或向右旋转一步,问最少的操作数使某一列均为1。如果不可能输出-1。

数据范围:n<=100,m<=105

 

Solution: 无解的判定:某一行全是0那么就无解。

预处理出每一行把某一列置1的最小操作数,这个可以在O(N)的时间内完成,接着枚举每一列,然后把所有行的这一列置1的答案加起来,最后取最小的即结果。复杂度O(N*M)。

 

F:cf 142 div1 B

题意:给一个n个点m条边无向图,从一个点到另外一个点需要耗费一定的时间,每个点都有一个禁止出发的时间点的集合,如果在这个时间内在这个点,必须等到能出发才能出发去其他点,问从1到n的最短时间。

数据范围:n,m<=105。禁止出发时间点<=105。 

 

Solution:用spfa算法算从1到n的距离,有一个地方需要改动,每次从队列中拿出来一个点的时候,原算法是直接从这个点的时间去推出其他的点,现在如果这个时间是不可用的,就把这个时间往后推一个,一直到可用为止,然后继续按照原算法推出其他节点的信息。因为不可用的时间点最多就105个,所以最多往后推105次,复杂度为O(e+105)。

 

G:cf 142 div1 C

题意:一个n个点的完全图,一方把m条边涂成蓝色,其余的边均为红色,问全为红色和全为蓝色的三角形总数是多少。

数据范围:n,m<=106

 

Solution:三角形就三种:1.全红,2.全蓝,3.1红2蓝或1蓝2红。求1+2即求总三角形数量-3的数量。

         总三角形数量:C(n,3)。

         3三角形数量:观察可知对于3的两种情况,均有两个点为一红边一蓝边交成,那么我们只要求出有多少个这样的点,再除以2,显然就是3三角形的数量。枚举每个点作为红蓝交成的点,那么交的方案有从这个点出发的红边*从这个点出发的蓝边这么多,把所有点的数量加起来,除以2,就是3三角形数量。

         复杂度为枚举点的复杂度O(N)。

 

H:cf 138 div1 A

题意:给出一串长为n的括号序列,由()[]构成,问你其中合法的有最多对[]的序列。

数据范围:n<=105

 

Solution:用栈扫描一遍序列,可以记录出每个右括号匹配的左括号的位置,及这段序列是否合法,这一步可以用O(N)的时间完成。

         接下来只要再扫描一遍序列,找到所有合法的最长序列然后统计这个序列中中括号的数量更新答案就可以了,因为每段合法序列只会属于一个最长序列,所以复杂度为O(N)。

         总复杂度为O(N)。序列小情况和细节很多,这里不赘述。

 

I:cf 138 div1 B

题意:给了一个母串和一个子串,问母串中的任一个字符是否都能属于某个和子串相等的子序列。

数据范围:序列长度n<=2*105

 

Solution:记录母串的每个字母从前开始最多能匹配到哪个地方,从后开始最多能匹配到哪个地方,设为p1和p2,如果p1>=p2那么这个字符可以作为某个子序列的元素,否则不行。比如母串为aaabb,子串为aaa,那么母串中的第一个a:p1=1,p2=1,第二个a:p1=2,p2=2,第三个a:p1=3,p2=1.所以三个a都是可行的。

至于如何求p1和p2,以p1为例,因为只有26种字母,可以记录每种字母可以匹配到子串的哪个位置,记为can[c]。扫描的时候,根据这个记录可以得到当前字母的匹配位置和子序列中下一个可行字母的位置,依此修改记录,维护每个字母最远可以匹配到多长。设当前为第i位,方程为can[s2[ans[i]+1]-'a'] = max( can[s2[ans[i]+1]-'a'] , ans[i] + 1 ),ans[i] =can[s1[i]-'a'] 。

复杂度O(n)。

 

J:cf 138 div1 C

题意:给一个n个数的序列,每次操作把第i个数变成前一次序列中前i个数的和,问执行k次以后的序列每个数mod 109+7等于多少。

数据范围:n<=2000,k<=109

 

Solution:n个数可以看成一个1*n的矩阵,然后转移矩阵也非常好构建,就是说如果n=100的话利用矩阵加速是很容易在时限内得到序列最后的样子的,但是n=2000的话矩阵乘法的n3的复杂度无法承受。

仔细观察矩阵,矩阵的下半部分全是0,其余满足g[i][j]= g[i-1][j-1],而且两个这样的矩阵相乘得到的结果矩阵显然也满足这个性质,那么我们可以干脆用第一行来代替整个矩阵,每次做乘法也只用求出结果矩阵的第一行就行了,这样矩阵乘法的复杂度降为O(n2),总复杂度为O(n2*logk)可以在时限内出解。

PS:该题还有一个利用组合数的O(n2)方法,做法不知。

 

K:cf 140 div1 C

题意:在斐波拉契数列的第l项到第r项中,选出k个数,让这k的数的最大公约数最大。

数据范围:1<=l<r<1012,1<=k<=r-l+1。

 

Solution:斐波拉契数列有一个很好的性质,gcd(f[i],f[j])=f[gcd(I,j)],这样题目就被转化成了:找一个最大的x,使l~r中至少有k个数是它的倍数,即r/x-(l-1)/x>=k成立。

设q= ,对于1<=x<=q,r/x最多有q*2个,对于q<x<=r,因为r/x<=q,所以r/x最多有q个,所以r/x的数量是q个数级别的。在r/x确定的时候,要让原式最大,则要让x最大,那么我们枚举所有的这样的r/x,算出此时最大的x,如果大于k就更新答案,这样就能找到最大的x了。然后利用矩阵乘法求出第x项斐波拉契数就是答案。

复杂度:O( +23*log )。

 

L:cf 139 div2 D

题意:给一个n*m的地图,图上有一条贪吃蛇,走的规则和死亡规则和贪吃蛇游戏一样,给了一个苹果,问贪吃蛇最少多少步可以吃到苹果。吃不到就输入-1(苹果被墙围起来了等情况)。

数据范围:n,m<=15,蛇的长度s<=9。

 

Solution:本题的基本思路是bfs,只是可以预见重复的状态非常多,盲目搜索无疑会超时,有一种方法就是把蛇做成一个结构体,参数为头,以及躯干每个点的坐标,然后用map来记录状态是否出现过,但是这样维护的常数代价很大,还是会TLE。再观察蛇的点,每一个点都会在前一个点的四个方向之内,那么我们可以用3个参数来表示一条蛇,头的x,y坐标,以及一个s-1位的四进制数,四进制数的每一位表示当前点在上一个点的哪个方向,这样的话蛇的移动可以用O(1)的时间来维护,最后第一次碰触到苹果时走过的次数就是答案。

 

M:cf 139 div2 E

题意:给了一个式子,问对于任意的正整数x和y,第n大的不可能出现的z是多少。

数据范围:n<=40。

 

Solution:假设x为奇数,x=2*t-1,那么z=t+y+2ty-y,z=t*(2y+1),如果z不存在,那么z+1必为2的幂,即z=2k-1。

        假设x为偶数,x=2*t,那么z=t+y+2ty,2z+1=2t+1+2y+4ty,2z+1=(2y+1)*(2t+1),如果z不存在,2z+1必须是质数,即2k+1-1必须是梅森素数。然后查表把k求出来就行。

 

N:cf 137 div2 C

题意:给一个分式,分母为n项a1,a2…an乘积,分子为m项b1,b2,...bm乘积,要求化简该分式,使分母和分子仍为一些数的乘积,但要求新的ai’和bj’均互斥,且每个数不能超过107,输出一种方案。

数据范围:1<=n,m<=105,每个数均<=107

 

Solution:如果能知道所有数的分解情况的话,就可以知道该分式最后每个素数有多少个,这样的话很容易对每个数进行化简,得到最后结果,这部分不赘述。

       关键是如何知道每个数的分解结果,如果每个数暴力分解的话,复杂度为O(N*),无法承受。不妨换种方式考虑。

       我们可以在O(n)的时间内筛除1-n中所有的素数,对于每个数,它只会被一个素数筛一次,之后就不会被素数筛到,那么我们记下所有的数是被哪个素数筛到的,那么这个数的一个素因子我们就知道了,这个数除以这个素因子,得到的结果是由哪个素数筛到的我们也知道,这样的话,我们得到一个数就可以一直根据这个信息直到它变成质数或者1为止,这样在这个过程中我们就得到了它的所有素因子。

       对于每个数我们这样做的复杂度为O(20),因为每个数最多有20个素因子,总体下来复杂度为O(N*20)。加上筛素数的时间,复杂度为O(107+N*20)。

 

O:cf 137 div2 D

题意:有n个人,告知n个第一场比赛和第二场比赛的分数,每个分数属于某一个人,每人的分数为两场分数的和,最后按每人的分数进行排名,现在告诉你一个人最少得了x分,问他最好和最差的名次。

数据范围:n<=105

 

Solution:最好的名次肯定是第一名,问题是如何确定最坏的名次,要名次最坏,就要分数大于等于x的组合最多,把第一场和第二场的名次排序,第一场从小往大,第二场从大往小,如果分数和大于x,ans加一,否则在第一场中选更大的一个和当前第二场的分数进行组合,直到序列末尾,最后ans就是答案。

复杂度为排序复杂度O(nlogn)。

 

P:cf 137 div2 E

题意:给出m对字母关系,后一个字母不能出现在前一个字母后面,问一个长为n的序列,满足m对字母关系的有多少个。

数据范围:m<=52,n<=1012。序列由小写和大写字母组成。

 

Solution:字母总数只有52个,很容易做出一个52*52的转移矩阵,然后做一个n次的矩阵乘法就行,简单题。

复杂度:O(523*log(n))。

 

Q:cf 136 div1 A

题意:给你一个包含n个元素的数列,问你能不能最多通过一次交换使得整个数列呈非降序排列。

数据范围:2<=n<=105

 

Solution:读入数据后调用sort快排一遍,然后与原数据相比,发现有两个以上不同的数就输出NO,否则就输出YES即可。

 

R:cf 136 div1 B

题意:给一个长度为n个数的序列,m个操作,每个操作问从l到r的x的数量,x在l到r中正好出现了x次。

数据范围:n,m<=105

 

Solution:把所有询问按照左端点排序,维护一棵线段树,第i个位置表示从i到当前的末尾满足条件的x个数,然后从1扫到n,遇到一个数a,如果它出现的次数大于a了,那么设它是第k次出现,那么第k-a次到第k-a+1次出现的区间ans-1,第k-a+1到第k-a+2次出现的区间ans+1。遇到询问时,直接查询询问左端点的值就行了。需要的数据结构是一个支持区间更新和定点查询的线段树。复杂度O(nlog(nm))。

       还有一种很简单的方法,1出现1次,2出现2次…a出现a次,因为序列长度最多也就105,所以合法的数的个数不会超过,可以预处理出这些数,然后对于每个询问暴力查询就可以了。复杂度O(m*)。

 

S:cf 136 div1 C

题意:给两个n个数的排列a和b,每次b向左旋转一位,问n次a和b的相同的数的距离最近是多少。

数据范围:n<=105

 

Solution:维护两个优先队列,第一个存所有在a中对应数左边的数与a中对应数位置的差,另一个存右边的。每次操作都可以看成a中所有数+1,b中所有数-1,当b中有数被减到0时,将其插入a中,然后在a中二分找到当前要到队列末尾的那个数,从a中删除,插到b中。然后每一步的答案就是a中和b中最小数的min值。

       优先队列的插入,删除,查找都可以做到O(logn),但是全部加1和全部-1,是不能的,可以用平衡树做,但是考虑到本题操作的特殊性,把所有的数加一,不如直接设置一个lazytag,当有数要插入或删除的时候,再考虑lazytag修改后再操作,全部加一和全部减一的操作只用对lazytag进行操作就行了。

       复杂度O(nlogn)。

 

T:cf 134 div1 B

题意:黑板上有两个数a,b,每一步操作有两种选择,如果是T,a=a+b,如果是B,b=b+a,显然如果是TB连续的那么就是一个斐波拉契数列,现在题目告诉做了n次操作,及最终结果,但是中间有几次出错了,问能够得到该结果序列最小的差值是多少,差值定义为连续的T与B的个数。

数据范围:n<=106

 

Solution:直接枚举每一步是T还是B显然是不行的,复杂度高达2100000,就算能剪枝也很难在时限内完成,不如倒过来考虑,假设最后的两个数已经确定了,为a’,b’,如果a’>b’,那么最后的操作显然为T,否则为B,这样递归下去,可以反推出所有的序列,然后在所有合法的序列中选出一个差值最小的,这样做法就很明显了,暴力最后的另外一个数是多少,然后把这个序列生成出来,求出结果,并与ans取min。

                   考虑a>>b的情况,这时会有很多个连续的T,那么这种情况我们可以用类似求最大公约数的方法把中间这一段求出来,这样的话,一次模拟的复杂度为O(logn)。总复杂度为O(nlogn)。

ps:在原题中直接暴力求出序列也不会tle。

 

U:cf 134 div1 C

题意:给出一个表达式树,叶子节点有?,0,1,三种,运算符号有&,|,^。表达式最后的值为0和为1是可以知道的,现在有0和1两种叶子,但是搞混了不知道哪个代表0,哪个代表1,这个可以通过这个表达式树得知。现在给出一个长度为n的表达式树,问能否判断两种叶子哪个是0哪个是1。

数据范围:n<=106

 

Solution:设状态为f[i][s],i表示以i为根节点的子树,s表示该子树的状态,子树的状态有3种:

1.      存在一个序列x,使f[i][x]=0且,f[i][-x]=0,-x表示x序列的反序列,即所有元素的取值均取反。记此状态为1。

2.      存在一个序列x,使f[i][x]=1且f[i][-x]=1,记此状态为2。

3.      存在一个序列x,使f[i][x]=0,f[i][-x]=1或f[i][x]=1,f[i][-x]=0。记此状态为4。

显然叶子为0,1,?分别对应了状态1,2,4。这三个状态是相互独立的,一个节点可能同时具有3种状态。

因为不好描述,下面直接贴出转移了:   

int ret = 0 ;

   if ( c == '&' ) {

       if ( ( (x & 2) && (y & 2) ) )

          ret |= 2 ;

       if ( ( ( x & 1 ) || ( y & 1) ) || ( ( x & 4 ) && ( y& 4 ) ) ) 

           ret |= 1 ;

       if ( ( ( x & 2 ) &&  (y & 4 ) ) || ( ( x & 4 ) && ( y & 2 ) ) || ( ( x & 4 )&& ( y & 4 ) ) )

           ret |= 4 ;

    }

   else if ( c == '|' ) {

       if ( ( ( x & 1 ) && ( y & 1 ) ) )

          ret |= 1 ;

       if ( ( ( x & 2 ) || ( y & 2 ) ) || ( ( x & 4 ) && (y & 4 ) ) ) 

           ret |= 2 ;

       if ( ( ( x & 1 ) && ( y & 4 ) ) || ( ( x & 4 )&& ( y & 1 ) ) || ( ( x & 4 ) && ( y & 4 ) ) )

           ret |= 4 ;

    }

   else if ( c == '^' ) {

       if ( ( ( x & 1 ) && ( y & 2 ) ) || ( ( x & 2 )&& ( y & 1 ) ) || ( ( x & 4 ) && ( y & 4 ) ) )

           ret |= 2 ;

       if ( ( ( x & 1 ) && ( y & 1 ) ) || ( ( x & 2 )&& ( y & 2 ) ) || ( ( x & 4 ) && ( y & 4 ) ) )

           ret |= 1 ;

       if ( ( ( x & 3 ) && ( y & 4 ) ) || ( ( x & 4 )&& ( y & 3 ) ) )

           ret |= 4 ;

}

x,y表示左右子树的值。

复杂度O(n)。

 

V:cf 133 div2 C

题意:一个图书馆,要雇管理员,要求每天都要有至少k个管理员,每个管理员都是工作n天,然后休息m天,要求任意相邻两天之间都有同一个管理员工作,问最少需要几个管理员,他们应该在什么时候被雇佣。

数据范围:1<=n,m<=1000,k<=1000,n!=1。

 

Solution:分情况讨论:

if ( k != 1 ) {

       if ( n == m )

                            需要k*2+1个人,k个人在第一天,一个人在m+2天,k-1个人在m+1天。

       else

                            需要k*2个人,k个人在第一天,一个人在m+2天,k-1个人在第m+1天。

    }

   else {

       if ( n != m ) {

           if ( n - m >= 2 )

                                     需要2个人,一个第一天,一个第n天。

           else

                                     需要3个人,一个第一天,一个第n天,一个第n+1天。

       }

       else {

           if ( n != 2 )

                                     需要3个人,一个第一天,一个第n天,一个第n*2-1天。

           else

                                     需要4个人,一个第一天,一个第n天,一个第n*2-1天,一个第n*2天。

       }

}

证明略。

 

W:cf 133 div2 D

题意:给你一个蜘蛛网的建立方法,即从顶点开始发散出去n条线,把平面分成了n个区域。每个区域都有ki个相互平行的线段,线段与相邻的两射线的交点(称为结点吧)到顶点的距离相等。这样可以把一个区域划分成ki-1个梯形。如果一个梯形两腰包含的结点个数不相等,就说这个梯形是一个不稳定的梯形。然后统计蜘蛛网的所有不稳定梯形的个数。

数据范围:3<=n<=1000,1<=ki<=105,1<=pij<=105,pij表示第i个空间的第j条线段到定点的距离,距离即其中一个结点的距离。

 

solution:对于每一条射线所涉及的两组结点合并按照距离排序,然后离散化距离为1,2,3...,对于每个梯形,只需要比较两腰所在的线段的结点距离差是否相等,即可判断该梯形是否稳定。把结果加起来即可。

hint:不需要开1000*10^5的数组,只把第一组和第二组数记录下来即可,因为中间的变量并不需要保存。

 

X:cf 133 div2 E

题意:一个数字的根定义为g(x)=g(x的所有位之和),直到x只有一位数,现在给一个n位的k进制下的数字,问数字根恰好为b的子序列有多少个。

数据范围:k<=109,n<=106

 

Solution:数字根一个很好的性质就是在k进制下的一个数,他的数字根就是这个数mod k,那么我们就直接求出前i个数的和s,然后对于数字b,查询之前的数字根为s-b的这样的数有多少个,然后把当前的数字根记录进去,统计答案即可。要注意数字根为0的情况的特殊处理。

复杂度O(nlogn)。

 

Y:cf 147 div2 D

题意:略。

 

Solution:思路很简单,T树最优的话T树里每个节点必然由原来的2个节点构成,把每个中间节点和每个孩子组合形成一个节点,相同中间节点的视作兄弟,相邻节点的视作父子,按照左孩子右兄弟的方式建一颗树,就是一个合法的T树。

复杂度O(n)。

代码链接

原创粉丝点击