这2天一直在sgu上做题,上面的题目短小精悍,对数学的要求比较高,至少不用浪费大量的时间去读题。以后还会一直补充这份题解。
100 超超级水题,直接输出
101 实质是求无向图的欧拉路径(或者欧拉回路)
102 利用欧拉函数
欧拉函数是指:对于一个正整数 n ,小于 n 且和 n 互质的正整数(包括 1)的个数,记作 φ(n)
有关性质:
对于素数 p ,φ(p) = p -1 。
对于两个不同素数 p, q ,它们的乘积 n = p * q 满足 φ(n) = (p -1) * (q -1) 。
欧拉定理 :
对于互质的正整数 a 和 n ,有 a^φ(n) ≡ 1 mod n 。
费马定理 :
若正整数 a 与素数 p 互质,则有 a^(p - 1) ≡ 1 mod p 。
欧拉函数公式:
p^k 的欧拉函数:对于给定的一个素数 p , φ(p) = p -1。
则对于正整数 n = p^k , φ(n) = p^k - p^(k -1)
p * q 的欧拉函数:
假设 p, q是两个互质的正整数,则 p * q 的欧拉函数为
φ(p * q) = φ(p) * φ(q) , gcd(p, q) = 1 。
任意正整数的欧拉函数
任意一个整数 n 都可以表示为其素因子的乘积为:
I
n = ∏ pi^ki (I 为 n 的素因子的个数)
i=1
根据前面两个结论,很容易得出它的欧拉函数为:
I
Φ(n) = ∏ pi^(ki -1)*(pi -1)
i=1
103 此题最一般的模型是拆点,将图中一个点拆成多个点。这个题可以直接用dijsktra求最短路,只是 在扩展节点的时候有点不同,这目前选出的到达时间最小的节点为k(到达时刻为w[k]),枚举其下一 个节点i,如果k和i在w[k]时刻的状态相同,则更新w[i]。否则向下推一个状态,如果下一个状态k和i的状态相同,那么再继续往下推一个状态,如果k和i的状态仍然相同,则k和i之间不可能通行 了。否则更新w[i]的值。
104 基本的线形动态规划(2维状态)
105 自己推出规律,非常简单的。
106 扩展欧几里德算法,要注意许多特殊情况
107 首先利用搜索求出平方后尾数为987654321只有8中情况:
111111111
611111111
380642361
880642361
119357639
619357639
388888889
888888889
这样程序就异常简单了,如果为数大于9位,第一位只有9个数字可填,第二位到倒数第10位都有10个数字可填。
108 好题!!!
利用筛选法(跟筛选质数差不多) ,选取第一个self-number:a,然后将d(a)剔出掉
此题关键是有内存限制(才1MB),因此不能开那么大一个标记数组,注意到由于数字的为数最多有7位,因此d(a)-a<=7*9=63,因此我们只需要 开一个64的标记数组就可以了,利用为操作a&63来取标记位置.
另外由于时间限制,要将计算一个数字各位和的时间减少,我们可以开一个1000大小的数组sumdig,预先处理一下记录1000以内的数的各位数字和,这样求一个数的各位数字和就只需要O(1)的时间了:sumdig[a/1000]+sumdig[a%1000]
109 构造法,先走N步,便可以把副对角线右下的那条对角线以下的所有数删除掉,以后每次都走奇数步,将没有删除的数中可以将最右下的那一条对角线删除掉,如此进行n+1次,便仅剩下第一个位置了。
112 直接用高精度乘法和减法就OK
113 这道题关键在于快速判断一个数是否为素数,可以用经典的随机算法进行素数测试(效果非常好)。原理是:如果a是素数,那么由费马定理,有对于任意n属于(1..a-1),有a^(n-1)≡1(mod n),这样我们可以随机 生成(1..a-1)的一个数,看a^(n-1)mod n 是否等于1。由于n可以达到10^9,所以也需要快速求a^(n-1) mod n,这个可以通过二进制的幂模算法来求(二分),算的次数只需要log(n)。
114 邮局位置问题(带权中位数)
对于n个元素x1,x2,...,xn,每个元素都有一个权值w1,w2,...,wn,sum{wi,1<=i<=n}=1,这n个元素的带权中位数是这个元素,sum{wi,xi<xk}<1/2,并且sum{wi,xi>xk}>=1/2
求n个元素的带权中位数可以这样求:
先对n个元素按照x从小到大排序,然后:
Weighted-Median(A)
k<-1
s<-0
while s+w(k)<1/2
do s<-s+w(k)
k<-k+1
return k
时间复杂度是O(nlogn),注意到我们根本不需要对所有的元素排序,因为我们只需要求出”中间“的元素,可以用典型的求中位数的算法,利用二分,然后select,时间复杂度是O(n),但是系数很大,因此一般情况下还是用O(n)的普通算法。
本题的原形式邮局位置问题,在x轴上有n个邮局,每个邮局有一个值wi,以及横坐标pi,现在需要求出一点p,使得sum{wi*d(p,pi),1<=i<=n}最小,其中d(a,b)=|a-b|.
现在来具体求解这个问题,最后可以看到这个问题可以转换成求带权中位数问题。
设c(p)=sum{ wi*d(p,pi),1<=i<=n}
将其换一种解法,c(p)=sum{wi*(p-pi),pi<p}+sum{wi*(pi-p),pi>p}
由于当pi=p时wi*d(p,pi)对于c(p)没有贡献,因此省略掉。
现在就是要球c(p)的最小值,对c(p)求导,dc/dp=sum{wi,pi<p}-sum{wi,pi>p}
可以看到dc/dp是非递减的,因为当p增大时,sum{wi,pi<p}不会减小,而sum{wi,pi>p}不会增加
当p<min{p1,p2,...,pn}时,c(p)<0;当p>max{p1,p2,...,pn}时,c(p)>0。
因此,必定存在p',当p<p'时使得dc/dp<=0;当p>p'是dc/dp>=0.因此p'即位使得c(p)取得最小的p值。
现在证明带权中位数y就是p',对于所有的p<y,p不是带权中位数,有
sum{wi,pi<p}<sum{wi,pi>p} 因此dc/dp<0
对于所有的p>y,p不是带权中位数,有 sum{wi,pi<p}>sum{wi,pi>p} 因此dc/dp>0
因此,对于所有的p不等于y,有c(p)>c(y)
所以本问题的最优解即为带权中位数。
115 简单题,直接模拟求
116 素数测试+背包问题
首先求出所有的super prim,这个需要求出10000以内的所有素数,可以用随机素数测试加快速度。
然后问题就变成了判断一个数能否表示成201个super prim中若干数的和,经典的背包问题。
117 二进制的幂模算法(二分)
118 这题很容易看出(猜测)有这样的结论:f(a*b)=f(f(a)*f(b))
f(a+b)=f(f(a)+f(b))
于是这题就非常简单了,从头到尾将数据读入,一边读一边算,连数据都不用存储下来。
119 数论题。还要仔细想一下
120 考查点比较多的计算几何
先求出多边性中心的坐标(设为P0),求这个坐标我是这样求的,P0和已知的两个点P1,P2组成一个等腰三角形,并且底角可以算出来,这样就可以求出P0P1的长度,即外接圆的半径,然后通过定比分点公式算出P1P2上面的一点P3坐标,使得P3P1=P0P1,然后将向量P1P3逆时针旋转底
角的读数,就可以得到圆心P0的坐标了。
有了P0的坐标就好办了,从P1开始,每次都将向量P0P1顺时针旋转一定的度数,便可以得到所有点的坐标了。
这题要用到向量的旋转,相关文章在本blog可以找到。
·121 比较好的图构造题!!! 充分利用DFS生成树!! 还要要仔细想一想
!!122 右题意知对任意定点x,y,有deg(x)+deg(y)>=n,有个定理,可证明满足这样条件的无向图必定存在一个哈密顿回路,并且证明过程就是基于构造的,所以直接将证明的方法拿来写程序就可以构造一条贿赂了。
PS:我自己找着离散书敲半天硬是没敲出来。。太衰了,直接拿别人的一个pas版交上去了,唉。。。
123 简单题,直接算(这种题可以将数据加强,就不能直接算了,要自己将递推式写成矩阵相乘的形式,然后利用矩阵相乘的结合率利用二分来求)
124 判断点在多边性内,经典问题。
从那个点向右作射线,判断该射线与多边性边相交的数目是否是奇数(注意特殊情况的处理)
125 直接搜索+剪枝(每当枚举完一行就进行判断剪枝)
126 数学题,有点像倒水问题
注意到每次都使小的那个数翻倍,设t=(a+b)/gcd(a,b),则如果t不为2^n,则无解,
否则次数为log2(t). (需要严格证明一下!!!)
分别用所占比值来表示两个箱子中球的个数(a1/b1, a2/b2)(其中a1/b1为既约分数)。从(0, 1)倒推可以发现,所有结点都满足b1, b2是2的倍数。而次数既为log2(max(b1,b2))。
127 简单题,直接模拟
130 很容易知道当k条线不相交时所分的部分最小,为n+1
现在要求有多少种分法,用DP.设f[i]表示从第1个点到第i个点的分法数,则
f[i]=sum(f[j]*f[i-j+1]) ,0<=j<i;
最后的结果就是f[n]
133 很简单的题,直接求.
134 求树种的中心点,用动态规划。
设f[i]表示以i为根的子树中结点总数,c[i]表示删除结点i后的分散的树中节点数最大的值。
则f[i]={ 1,i为叶子
{ sum{f[j]},j为i的儿子
c[i]={ n-1,i为叶子
{ max{f[j],n-f[i]} j为i的儿子
133 先将先端按照左端点排序,然后从左到右将先端扫描一遍就OK了。
我一开始担心long存不下10^9,用了double,狂超时,后来算了一下2^32大概是5*2^32,于是改用long,于是就AC了,看来double的运算式超级龟速啊!
135 简单题,直接用数学公式
136 解方程。设n个坐标横坐标分别为a1,a2,a3,...,an,则有
a1+a2=2*x1
a2+a3=2*x2
...
an+a1=2*xn
首先明确一点,如果我们求出一点的坐标,那么其他所有点的坐标都可以推出来。
先求第一个点的坐标。
当n为奇数时,解得: 2xn-2x(n-1)+2x(n-2)-…+…-a1=a1,此时必有解
当n为偶数时,解得: 2xn-2xn-1+2xn-2-…+…+a1=a1,a1有解或者有无数组解
139 判断15数码问题是否存在解。利用置换群的理论,可参看《组合数学》,主要思想是偶置换不可能用奇置换得到,奇置换不可能由偶置换 得到。
!!142 比较经典的一题(数学证明+字符串编码hash)
143 简单的树形dp
144 画图,可以转化2维坐标系中曲线构成的面积,利用积分算出公式: 设s=(y-x)*60,则结果为(2*z*s-z*z)/(s*s)
146 简单题,直接求
149 经典题,求树中任意两个节点的最长距离(树形dp)
设dp[i][0]表示从节点i出发,终点在i的子树中的最长距离
dp[i][1]表示从节点i出发,终点在i的子树中的次最长距离(仅次于dp[i][0])
dp[i][2]表示从节点i出发,终点不在i的子树中的最长距离
于是有dp[i][0]=max{dp[j][0]+cost[i,j])
dp[i][1]=max{dp[j][0]+cost[i,j]) 所选用的最优决策变量j与dp[i][0]所选用的不同
这两个值先算,在树中按照从底向上的顺序算
再算dp[i][2]
如果father[i]的dp[father[i]][0]的最优决策变量j为i,由于不能选择i,于是
dp[i][2]=max{dp[father[i]][2]+cost[father[i],i],
dp[father[i]][1]+cost[father[i],i]}
如果father[i]的dp[father[i]][0]的最优决策变量j不为i,那么
dp[i][2]=max{dp[father[i]][2]+cost[father[i],i],
dp[father[i]][0]+cost[father[i],i]}
这个按照从上到下的顺序算,算完之后
对于节点i,最长距离为max(dp[i][0],dp[i][2])
152 直接先全部取整,不够的话再向上取整。
154 n!的末尾的0的位数为n/5+n/25+n/125+n/625+...(各项都取整)
题目是要已知末尾0的个数反过来求最小的n,在纸上画一下就可以找到大概的规律了(利用分段求和)
163 直接将a^b>0的加起来
168 直接分析b[i][j]的表达式,可以看出b[i][j]是在a矩阵中一定区域中的最小值,按照一定的顺序来求可以做到o(n^2)的复杂度
169 又是一道数学题
以4位数n=(a,b,c,d)为例,由于p(n)!=0,所以d不能等于0,也不能等于9,故n+1=(a,b,c,d+1)
由于n mod p(n)=0,故a*10^3+b*10^2+c*10+d=k1*a*b*c*d;
同理,a*10^3+b*10^2+c*10+d+1=k1*a*b*c*(d+1)
将第一个式子代入第二个式子,得(k2+(k2-k1)*d)*a*b*c=1
由于a,b,c,k1,k2都大于等于1,故上式成立当且仅当a=b=c=1
推广到一般的情况,设有k位数,则满足条件的k位数的前n-1为必然为1
这样,我们只考虑最后一位d,使得数字111...111(k-1位)d能够被d整除
既11111..111(k-1位)*10能够被d整除(d属于1..8)
170 贪心,直接算出两个串中'+'号(或者'-'号)的距离之和
172 直接dfs,不同连通分量之间没有影响。对于一个连通分量,可以随便找个顶点染个色,然后dfs进行染色,如果发现矛盾,则无解。
175 递归求解。设solve(a,b)表示在长度为a的串中第b个字符在加密后的串中的位置,设mid=[a/2],则有
{ 1,a=1
solve(a,b)={ a-mid+solve(mid,mid+1-b), b<=mid
{ solve(a-mid,a+1-b), b>mid
!!178 这题的题意理解比较困难,其实是说,一条由N个环相互扣起来的链子,最少要破坏几次,就能在破坏后小链中选择一部分,用它们包含的环的个数表示1至N的所有整数。当然,本题中的“破坏”比较奇妙,由于环是相互扣起来形成链子的,而每次破坏,就是把一个环打开,所以破坏一次就会得到一条链的左边,右边,和中间(仅剩一个环)3部分。假设最后要破坏x次,那么显然就会得到x个仅有一个环的“链子”,所以为了尽可能的多的表示不同的数目,其他链子的长度应该为(x+1),(x+1) * 2,(x+1) * 4等等。由于破坏了x次,所以可以得到(x+1)段链子,最长的一段可长达(x+1) * 2x,所以当前最大可表示能力为[(x+1)*2^(x+1) – 1],只要这个式子不小于N,显然就一定能够找到破坏方案。而实际上就等于求一个不等式的最小整数解:[(x+1) * ^2(x+1) – 1] ≥ N
而显然答案在log2N级别,所以只要从1开始向上一次枚举x的取值就可以了。
!179 既然是要“合法序列”,那么一定要有N个“(”和N个“)”。并且从左往右数到任意一个位置,“(”的个数都不能小于“)”的个数。
算法其实很简单的。从右往左数,找到第一个可以变为“)”的“(”,并将其改变,而后面的,只需修改为“(((…((())))…))))))”的形式即可。
180
求逆序对数,有很多方法可以求,比如归并排序,分段哈希表,线段数,树状数组,其中后三者的思想都是一样的。由于本题每个数可以达到10^9,因此哈希表和线段数都不适用,我采用的是树状数组。先求出这样一个数组,index[],index[i]表示输入数组中的第i个数在输入数组按照从大到小的顺序排序后的数组中的位置。index的求法是:先求出p数组,使得当i<j时,a[p[i]]<a[p[j]],这样,index[p[i]]=i;要注意到p[i]=p[i-1]的情况这样,当我们读入第i个数后,能形成的逆序对的个数就是小于index[i]出现的次数,这个问题便是用经典的树状数组来统计。
WA了3次,原因:long long不能用printf("%ld")来输出,要用cout输出;或者用__int64类型,用printf("%I64d")输出
181 由于M最多为1000,所以A序列中最多有1000个不同的数,必然出现循环。算出循环的起始位置和循环长度即可.
184 超级水题
188 树形dp
193 首先明白一点,k为奇数时一定满足条件(不是说为偶数就不满足条件)。所以,当n为奇数时,去k=(n-1)/2就可以满足条件,并且这样的k最大。当n为偶数时,k<=n/2,首先k不可能等于n/2(除了n=2外),因为若k=n/2,那么序列为0,n/2,0,那么1就没有被包含进去。所以k<=n/2-1,如果k=n/2-1为偶数则必然不满足条件,应为k为偶数,n也为偶数,那么序列中所有的数就必然为偶数了。所以当n/2-1为偶数时,结果就是n/2-
2,当n/2-1为奇数时,结果就是n/2-1.
196 设原矩阵为Aij,转置后的为Bij,那么最终结果的矩阵Cij=sum{Bik * Akj}=sum{Aki * Akj}
由于Aij=1代表i是第j条边的一个顶点,故Cij的值代表的意思是有同时在第i条边和第j条边上的点的数目,可知Cij只可能为0或者1,那么要计算所有Cij的和,我们要枚举所有的2条边吗?这样肯定超时。注意到当Cij=1时当且仅当这两条边公用一个顶点,因此我们可以先算出每个定点的读deg[i],然后计算出所有的2Cdeg[i](组合数),由于Cij=Cji,因此要将结果乘以2,这样还会漏掉一种情况,就是i=j时,这种情况其实
就是顶点数乘以2
222 公式C(n,k)*A(n,k),注意当k=0时,结果应该为1
231 A+B=C,A,B,C都为质素,A<=B,由于除2以外的质素都是奇数,并且奇数+奇数=偶数,不A必然不等于。这样,可以先用筛选法求出所有的质 数,然后枚举B,看2+B是否为质素即可。
276 just do as the problem said
302 直接用stack模拟