+1。显然{Fn}的通项式为Fn=2n。再把荷叶的作用考虑进去,得出利用n个石墩,m片荷叶可以过河的青蛙数为2n(m+1)。
∵arctan(1/a)=arctan(1/b)+arctan(1/c)
显然,1~n的每个数的约数个数之和,等于这些数中约数1的个数,加上约数2的个数……一直加到约数n的个数。即答案为
。
(其中xi为常数)叫做k阶常系数线性递推关系,求满足此递推关系的数列的第n项的最正点的方法是矩阵乘法(简称矩乘),其复杂度为O(k3logn)。当然,这类数列通常增长都比较快,因此往往求的是第n项除以某个数的余数。
。矩乘的编程实现是十分简单的。
Miller-Rabin算法是很慢的,在本题规定的时限内用Miller-Rabin算法判断前500个Fib数是否为素数几乎不可能。但我们可能通过这样一条定理来省去不少Miller-Rabin测试:
若m是n的倍数,则Fib[m]是Fib[n]的倍数。
证明:当n<=2时,由于Fib[n]=1,结论显然成立。当n>=3时,设Fib[n+1] mod Fib[n]=x,则Fib[n+2] mod Fib[n]也等于x,由此可得Fib[n+i] mod Fib[n]=Fib[i]*x mod Fib[n]。所以Fib[n的倍数] mod Fib[n]=Fib[n] mod Fib[n]=0。
由此可以得出:当n>4时,若n为合数,则Fib[n]必为合数。因此本题的最终算法是这样的:
对于一个比较小的Fib数,不必用Miller-Rabin测试,直接使用普通的素数测试法即可。对于一个比较大的Fib数,先检查它的下标,若是素数再进行Miller-Rabin测试。
最后献上一条温馨提示:由于本题const的人比较多,因此n的范围在不断被扩大。猫老大说了,只要发现一个const的就加大一次数据范围。因此在程序中不要把maxn设为500,要留一点余地,比如设成1000哦:)
用sum[i,j]表示把从第i个字母开始的连续j个字母放到同一个键上的代价。这个数组可在平方级的时间内算出来。用cost[i,j]表示在i个键上放j个字母的最小代价,cut[i,j]表示在代价最小时,前i-1个键上放置的字母的个数。很容易写出状态转移方程:cost[i,j]=min{cost[i-1,k]+sum[k+1,j]},i-1<=k<=j-1,使cost[i,j]取最小值的最小的k(因为要求最后一个键上的字母尽可能多)就是cut[i,j]。这种DP的时间复杂度是立方级的。
但本题的时间复杂度还可以降至平方级。这需要用到下面这个不等式:cut[i-1,j]<=cut[i,j]<=cut[i,j+1]。这个不等式的直观理解就是:当键减少一个时,最后一个键上的字母数不会比原来少;当字母增加一个时,最后一个键上的字母同样不会比原来少。下面给出证明:
先证cut[i-1,j]<=cut[i,j]。我们采用反证法,即证若cut[i-1,j]>cut[i,j],那么不等号右边对应的方案不是最优方案。右图表示四种不同的划分方案,每条线段表示j个字母,线段的第i段表示第i个键上的字母。我们把线段上标出的点称为分点。方案一为i-1个键,j个字母的最优划分方案,对应着不等号的左边;方案二为i个键,j个字母的某种划分方案,且满足cut[i-1,j]>cut[i,j],它对应着不等号的右边。我们称线段的每一段(包括左分点,但不包括右分点)为一个区间。由于方案一中分点的个数比方案二少一个,故由抽屉原理,方案一中必存在这样一个区间,它在方案二中对应的位置有至少两个分点,而这个区间显然不是最后一个区间。设这一个区间为[A,B),它在方案二中对应段上最右面两个分点称为a,b。我们仿照方案一、二构造了方案三、四。由于方案一是i-1个键,j个字母的最优划分方案,故cost1<cost3(用costi表示第i种方案中某些字母的代价,这里指所有字母的总代价)。然后我们比较cost3-cost1与cost2-cost4的大小:
对于绿色部分左边的部分,cost3=cost1,cost2=cost4,故cost3-cost1=cost2-cost4;
对于绿色部分右边的部分,cost3=cost2,cost1=cost4,故cost3-cost1=cost2-cost4;
对于绿色部分, cost3=cost2,cost1>cost4,故cost3-cost1<cost2-cost4。
三部分相加,得cost3-cost1<cost2-cost4。因为cost1<cost3,所以cost2>cost4,所以在i个键,j个字母的划分方案中,方案四比方案二优。证毕。
再证cut[i,j]<=cut[i,j+1]。同样采用反证法。右图表示四种不同的划分方案,方案一为i个键,j个字母的最优划分方案,对应着不等号的左边;方案二为i个键,j+1个字母的某种划分方案,且满足cut[i,j]>cut[i,j+1],它对应着不等号的右边。方案四的代价与方案一的代价之差等于方案四中多出的一个字母的代价,方案二的代价与方案三的代价之差等于方案二中多出的一个字母的代价,显然前者小于后者,即cost4-cost1<cost2-cost3。又因为cost1<cost3,所以cost2>cost4,所以在i个键,j+1个字母的划分方案中,方案四比方案二优。证毕。
有了这个不等式以后,我们可以按照i递增,j递减的顺序来计算min和cut数组。这样计算每一个元素时,它所用到的元素(min[i-1,*],cut[i-1,j],cut[i,j+1])都已求得。当然,在确定cut[i,j]的循环范围时,不要忘了i-1<=cut[i,j]<=j-1这一限制条件。下面进行时间复杂度分析。如果我们画一个直角坐标系,i轴向右,j轴向下,则每一个呈“捺”状的斜行对应的元素的cut值的范围恰好连成0..j-1,即计算每一个斜行的时间复杂度为线性的。这样的斜行的数量与问题规模同样成线性关系,因此DP的时间复杂度是平方级的。求sum数组的时间也是平方级的,故整个算法的复杂度仍为平方级。
把遗传特征看成一个有向图。找出一个包含所有特征的基因段,即遍历图中所有的边。显然,为了使基因段最短,我们应该用最少的笔数遍历图,因为每画一笔,经过的点数就要比边数多1。于是,我们只要求出整个图需要几笔画成,再加上图中的边数,就得到答案。
求一个图需要几笔画成,可以一个连通块一个连通块地计算。对于一个连通块,若它的每个点的入度和出度均相等,则它仅需一笔画成;否则,所需笔数为所有出度大于入度的点的出度与入度的差之和。
本题的模型是把起点、终点和所有的测量点看成一个图中的点,两点之间的边权为在这两点之间直线修路的费用,然后求从起点到终点的最短路。实际上这个模型是有问题的,因为有可能存在一种情况,转弯点设在山脚下,但并不在测量点。虽然题目禁止了这种方案,但我们可以将转弯点稍稍移动一下,使它离开山脚,但总费用的变化可以忽略。在此,我只能讲一下这个不完全正确的模型的解法。
求最短路用Dijkstra算法即可。关键的问题在于如何求在两点间直线修路的费用。为了求出这个费用,我们需要求出这条路在每座山内部的路程。对于一座山,我们可以求出路与山的各条边的交点。如果山的顶点也恰在路上,那么这些顶点也算在交点内。这些交点将路分成了若干段,现在我们要做的就是求出每一段是否在山内。扫描线法是容易想到了,但这种方法并不好,因为我们不能说一个是顶点的交点两侧的路段必定一个在山内,一个在山外。于是我们换一种思路:求出每一段的中点(其实段上非端点的任一点均可),判断这个中点是否在山内,以此断定这一段是否在山内。判断不在山的轮廓上的一点是否在山内,可以从这一点向远处一点(“远处”可以保证在山外)引线段,求这条线段经过的山的边数的奇偶性——若奇,则点在山内;若偶,则点在山外。为了保证这条线段不经过山的顶点,远处一点的坐标要选得不规则一些。上面的奇偶法只适用于不在山的轮廓上的点,因此在使用之前要判断点是否在边上(因为我们判断的点都不是交点,所以它不可能在顶点上),在边上的点认为在山外。
本题的运算量很大,所以要注意细节上的优化。尽量省略只使用一次的中间变量(如计算叉积、点积的函数只写一条语句),大约可以节省一半的运行时间。
由于给出了排序后方阵的最右一列,我们把这些字母排序(桶排即可)后自然可以得出方阵的最左一列。显然,方阵每一行的最后一个字母与第一个字母在原文中是相邻的(如果认为原文中最后一个字母与第一个字母也相邻的话)。我们自然产生了一种美好的设想:根据第一个字母就可以找到第二个字母,再根据第二个字母找到第三个字母……就这样按图索骥,就可以恢复出原文了!
对样例进行试验,成功。但这样做没有问题吗?有!看totoro这个单词:
| 构造字符串: totoro otorot toroto orotot rototo ototor | 将字符串排序: otorot orotot ototor rototo totoro toroto | 压缩结果: S'=ttrooo p=1 |
另外献上一点温馨提示:UNSOLVABLE的布局是不存在的。
本题没有什么统一的公式,所以还是要老老实实地分情况讨论。
首先枚举项数n。项数的最小值为3。那么最大值呢?我们来看极端情况:各项和一定时,欲使项数尽可能大,应使每项尽可能小。那么数列就应该是1,2,3……因此,若设各项和为n,应有n*(n+1)/2=s。把n=sqrt(s*2)代入,左边大于右边,因此方程的正根小于sqrt(s*2),故可用trunc(sqrt(s*2))作为n的最大值。
在项数确定了的情况下,我们可以求出数列每项的平均数m=s*2/n以及最大的公差d=trunc((m-1)/((n-1)/2))(公差最大时首项为1,且m与1的差是(n-1)/2个公差)。下面还要对n分奇偶讨论:
此题的模型就是:给定一棵带权无根树,求把它的任一条边的权改为0后,树中的最长路的最小长度。
105的数据规模启示我们使用DP算法。为了DP方便,我们以1号顶点为根,把无根树转化为有根树。我对每个顶点x定义了如下函数(哇,好多啊@:():
逆推。用T[i]表示第i项工作以后的工作所需的总时间,F[i]表示第i项工作以后的工作的总处理成本因素。设从0时间开始做第i项工作以后的工作所需的总成本为cost[i],则有状态转移方程:
cost[n]=0
cost[i]=min{(S+T[i]-T[j])*F[i]+cost[j]}(i<j<=n)
cost[0]为所求。
决策a比决策b优(a>b)的充要条件是:
(S+T[i]-T[a])*F[i]+cost[a]<(S+T[i]-T[b])*F[i]+cost[b]
<=> cost[a]-cost[b]<(T[a]-T[b])*F[i]
<=> (cost[a]-cost[b])/(T[a]-T[b])>F[i] (∵T[a]<T[b])
定义(cost[a]-cost[b])/(T[a]-T[b])为决策a、b间的斜率。维护一个决策队列,使它满足相邻元素间的斜率递增。当阶段为i时,先将决策i+1加入队尾,通过不断删除队列倒数第二个元素来维护它斜率的单调性。然后若队首两元素间的斜率小于F[i],则说明队首决策现在不是、将来也不会是最优决策,故将其删除。当队首两元素间斜率大于等于F[i]时,队首元素就是当前的最优决策了。
下文中,把输入的图称作G,选定的生成树称作S,S中的边叫做树边,其余的边叫做非树边。
为什么要修改某些边的权值呢?这是因为,把一条非树边Y加入S后,S中会出现一个环,而这个环上的某一条树边X的权值(w[X])可能比Y的权值(w[Y])大。为了使S成为最小生成树,就必须减小X的权值,增大Y的权值,以使w[X]<=W[Y]。如果把w[X]的变化量(当然是减小量)记作d[X],把w[Y]的变化量(当然是增加量)记作d[Y],那么我们可以列出不等式:w[X]-d[X]<=w[Y]+d[Y],即d[X]+d[Y]>=w[X]+w[Y]。
对于什么样的边对(X,Y),需要列这样的不等式呢?这样的边需要满足的条件是:把Y加入S后,X在形成的环上。也就是说,X在S中Y的两个顶点间的路径上。对于任一条非树边Y,为了找出需要与它列不等式的所有树边X,我们需要把S看成一棵有根树,求出Y的两个端点u、v的最近公共祖先a。分别从u和v向上走到a,对路上满足w[X]>w[Y]的边X都列出一个不等式。这一步求最近公共祖先,由于数据规模很小,朴素算法就可以,当然也可以用效率更高的Tarjan算法,详见1068题。
现在我们得到了一个不等式组,我们需要求出使所有变化量之和最小的一组解。观察这组不等式,发现它们与求二分图最大权匹配(参见1148题)时顶标所满足的条件形式完全相同。而求出最大权匹配后,顶标之和确实达到了最小。因此我们建立一个二分图B,把每条树边看成一个X顶点,每条非树边看成一个Y点。如果一个X点与一个Y点之间列了不等式,那么B中它们之间的边权就是不等式的右边,即它们在G中的权值之差;如果没有列,那么B中它们之间的边权为0。如果X点数与Y点数不相等,则补齐,并让它与所有异种点之间的边权全为0。然后用KM算法求出B的最大权匹配,这时匹配边的总权值(也就是所有顶点的顶标和)就是最小代价。同时,我们还求出了一种修改权值的方案:每个点的顶标就是它对应的G中的边的权值的变化量。
其实,上面“补齐”的顶点若是X顶点(一般情况下非树边比树边多得多),则它们根本不必考虑,因为它们即使匹配上了,对权和的贡献也只是0。因此,匹配完原有的X顶点就可以停止了。
长郡中学的王俊提出了一种更简单的算法。他把求最大权匹配的问题转化为求最大匹配。他的思路是:题目中只要求求出最小代价,并没有要求求出修改方案,因此把边上的权值转移到点上。他建立了一个新的二分图B',以所有的树边为X顶点,所有边(包括树边与非树边)为Y顶点。X顶点及边均无权值,Y顶点的权值为G中的边权。B'中的边是这样连的:B中每一条权不为0的边在B'中都有对应的边,另外每个代表树边的Y顶点都与代表同一树边的X顶点之间连一条边。这样做以后,B的完备匹配与B'的最大匹配之间就建立了一一对应的关系,且对应的两个匹配的权和之和为原图中所有树边的权值之和:
计算(i为左子树的结点数)。
P.S.如果谁有O(n2)或者更优的算法,请告诉我。
先解释一下题意:工厂每天都有一定的产品需求量,这些产品可以当天生产,也可以调用以前的库存。第n天的产品如果当前生产,那么生产每吨产品的花费就是cost[n];如果在第m天生产后存储到第n天,那么每吨产品的总花费就是cost[m]+s*(n-m)。
算法是很简单的。设第n天每吨产品的最小花费为best[n],则best[n]=min{cost[i]+s*(n-i)}(1<=i<=n)。显然best的递推式为best[1]=cost[1],best[n]=min{cost[n],best[n-1]+s}(n>1)。
下面的叙述中省略取模运算。
用DP进行预处理。设f[n,k]为有k个逆序对的n个数的排列的数目。当k<0或k>n*(n-1)/2时,规定f[n,k]=0。计算非0的f[n,k]时,考察数n的位置。如果它是最后一个数,那么它与其它数构成了0个逆序对;如果它是倒数第二个数,则它构成了1个逆序对……如果它是第1个数,则它构成了n-1个逆序对。故有
。
为计算方便,我们设s[n,k]为f[n,0]至f[n,k]的累加和(k<0时s[n,k]=0),这样状态转移方程便化为s[n,k]=s[n-1,k]-s[n-1,k-n]。我们不必计算f数组而可以直接计算s数组,当提问f[n,k]时,输出s[n,k]-s[n,k-1]。
注意到当a+b=n*(n-1)/2时,f[n,a]=f[n,b]。因此,数组的第二维下标的最大值不必设为maxn*(maxn-1)/2,而只需设为此值的一半。这样会节省一半的空间和一定的时间。你可以看一下本题的状态,我是第一个用少于1M的内存解决问题的:)
设输入的数的二进制表示中最右边的一组连续的1有n个,则把这组1前面的那一个0改成1,这一组1本身改为0,并把整个数的最后n-1位改为1。
用二进制数表示每个错误存在与否的状态,以及每个补丁的使用条件和使用效果,可以用位运算简便地求出每个状态可以使用哪些补丁及使用后的新状态编号。于是问题转化为求一个有向图中某两点间最短路的问题。可采用用堆优化的Dijkstra算法,复杂度为O(nlogn+nm)(n为状态总数,m为补丁数)。
宽搜求最短路。由于每个任一时刻至少有一个杯子是空的或满的,所以状态可以用一个二元组表示:(0,x)表示第1个杯子空,(1,x)表示第1个杯子满,(2,x)表示第2个杯子空,(3,x)表示第2个杯子满;四种情况的x均表示另一个杯子中的水量。两杯都空的特殊状态用(0,0)或(2,0)表示均可,两杯都满的情况类似。
把体重超过独木舟最大载重量一半的人叫做胖子,其余的人叫做瘦子。首先把所有的人按体重排序(当然用桶排,因为人可能很多,而体重的可能值却很少)。按体重递减的顺序处理每一个胖子,如果有瘦子可以跟他同乘一条船,则把他们安排到同一条船上,否则只好让胖子独坐一条船了。当胖子处理完后,剩下的瘦子就可以一对一对地上船了。
首先,同一队中的人必须满足一个条件:吃饭越慢的越排在前面。简单证明一下:假设两个人打饭的时间分别为a、b,吃饭的时间分别为c、d,其中c<d。假设第一个人排在前面,则第一个人吃完饭的时刻为a+c,第二个人吃完饭的时刻为a+b+d,这两个时刻的较大值为a+b+d(因为c<d)。如果把他们的位置对换,则两个时刻分别为b+d和b+a+c。显然这两个都小于a+b+d。因此我们先把所有人按吃饭时间降序排列。
然后使用DP算法。用buy[i]和eat[i]分别表示第i个人打饭和吃饭所需时间,sum[i]表示前i个人打饭所需的时间之和。设time[i,j]表示前i个人排队,第一队的总打饭时间为j时,最后一个人吃完饭的最早时刻。如果(i,j)是一种不可能的状态,则time值为无穷大。初始时只有time[0,0]为0,其余皆为无穷大。求time[i,j]时,对第i个人所排的队分情况讨论:
|
| 如图,把四面体放到坐标系中,使B点与原点重合,C点落在x轴正半轴上,D点位于xOy平面且y坐标为正,A点z坐标为正。B、C坐标很容易写出。利用两点间距离公式及BD、CD的长度,可以解出D点坐标。再利用两点间距离公式及AB、AC、AD的长度,可以解出A点坐标。四面体的体积就是dnz/6。 |