ACdream群原创群赛(2) A~F J 解题报告

ACdream群)原创群赛(2)

地址为:http://www.acdream.net/contest.php?cid=1009

 ACdream群原创群赛(2) A~F J 解题报告_第1张图片

出题情况:

         A~F 是我出的

     G~I 旋风小晴天

     J   [CUGB] wwj

     K   nanae

先说一下情况:

         貌似一开始大家被Board带歪了, F题我的题目比较坑爹是真的- -。。数学好的对数字敏感的同学上来扑肯定没错。。。我本来想的是应该会所有人先去搞晴天的G , 然后扑我的E 同时开 nanae 的K 。 这时候图论比较熟悉的同学应该已经开了J , 数论比较强的去搞HI。然后陆续会有人去搞B和C。最后过掉D就全体去扑A。这个流程还是比较科学的

 

A 题 Bella’s magic cube

题目类型:

         矩阵乘法 + 手写栈模拟

解法:

此题看到题目有Q <= 10^5 首先就应该想到矩阵乘法。

         建立 55 * 55 的矩阵,0~53号位置分别填写每个转化矩阵,在54号位置填写 1 作为改变某个地方值用。

         考虑到如果循环用递归会爆栈的问题,故模拟递归,建立两个矩阵栈分别代表颜色和数字,如果进入循环则进栈,否则退栈。

         需要注意的Trick是无时无刻出现的 /*  和 // 等组合。

 

B 题 Medicine

题目类型:

         搜索

解法:

         首先算出来所有数字的和, 然后判断是否有解。分k奇偶枚举公差之后,排序,就开始dfs搜索。

         需要的剪枝——

1、  如果每一堆的最小项小于药片的最小项 false

2、  如果当前一堆枚举的和sum加上剩下药片的最小大于当前堆的容量 false

3、  如果当前堆正好放下一大块药片,放

4、  如果搜索到某一个药片,并且前一个药片已经搜索过+前一个药片和当前一个大小一样,跳过(重要剪枝)

 

C 题 The bomb war

题目类型:

         简单hash + 二分(不是计算几何啊亲!!!!)

解法:

         由于(我特别标明了啊。。原来100使用One hundred 写的!!)武器的数量小于100,那么考虑把X坐标hash。然后开100个vector放Y坐标。对于每一个robot,遍历X,二分查找第一个大于Robot[y]的Mine[y] 和小于的Mine[y] 。记录,删除。复杂度是log(n) 。数据结构的话,一个set和一个map就足以了。


 

D 题 Numbers in line

题目类型:

         概率dp + 矩阵乘法 + 容斥原理

解法:

         很容易能够想到转移矩阵DP[10* 10] (就是原来的矩阵)。对于一开始的概率向量{0.1,0.1….0.1} Start, 我们只需要算——概率向量End = Start * {E + DP + DP^2 +…+DP^(n - 1)}就可以算出最后一个数字的概率向量End。至于矩阵等比数列求和的方法请见POJ 3233

         然后用容斥原理计算——每个数字至少出现一次的期望。即可。此题不卡精度


 

E 题 Mengzhu

题目类型:

         数学 + 乱搞

解法:

         利用精度 , 如果 A – B > 100 那么输出 A – 1 , 否则直接pow然后算

 

F 题 a + b

题目类型:

         数学 或者 高精度

分析:

比较坑爹的是 Mod = 1e10 + 7 ,于是两个longlong 直接相乘再取模的话爆longlong~

解法1:

         直接利用Java 高精度计算。不过每一步乘法需要都Mod一下

import java.math.*;
import java.io.*;
import java.util.*;

public class Main {
	public static void main(String[] args) throws Exception{
		Scanner cin = new Scanner(new FileReader("D:\\ACM\\出题\\20121111 Acdream 群赛\\Mengzhu's Math Problem\\input.txt"));
		PrintWriter pw = new PrintWriter("D:\\ACM\\出题\\20121111 Acdream 群赛\\Mengzhu's Math Problem\\output.txt");
		int re = cin.nextInt();
		for (int ri = 1; ri <= re; ++ri) {
			BigDecimal a = cin.nextBigDecimal();
			BigDecimal aa = a;
			BigDecimal b = cin.nextBigDecimal();
			if ( a.compareTo(b) < 0 ) {
				BigDecimal t = a; a = b; b = t;
			}
			BigDecimal d = a.subtract(b);
			if ( d.compareTo( BigDecimal.valueOf(1000) ) < 0 ) {
				//System.out.println(ri);
				double t = Math.pow(2.0, -d.doubleValue() ) + 1;
				t = Math.log(t) / Math.log(2);
				a = a.add(BigDecimal.valueOf(t));
			}
			a = a.subtract(BigDecimal.valueOf(1));
			a = a.setScale(9, RoundingMode.HALF_UP);
			if (a.subtract(aa).compareTo(BigDecimal.valueOf(2)) >0) System.out.println(ri);
			pw.println( a );	
		}
		pw.close();
	}
	//static Scanner cin = new Scanner(new BufferedInputStream(System.in));
}


解法2:

         按位计算。以下是两个LongLong 相乘的代码

LL mul(LL a, LL b, LL c){
    LL r= 0;
    while(b){
        if(b&1) if((r+=a) >= c) r-=c;
        a<<=1;
        if(a>=c) a-=c;
        b>>=1;
    }
    return r;
}


J 题 I can't describe the problem [Author : wwj]

题目类型:

图论

解法:

观察数据范围,点的个数和体力值都很小,那么可以开一个二维数组dis[i][j]代表到达i点剩余体力为j时的最短距离。然后就可以进行SPFA,状态的转移就两种,一种是走普通边,不需要花费体力,另外一种就是进行拆边。

拆边的方法其实也很简单。观察到边的大小只有10000,那么在刚开始我们就可以对1到10000直接二重循环枚举出所有的满足条件的a, b, c存起来。注意对每个值只保留最小是不对的。因为在魔法边上花费的体力是不跟边权成正比的。

当然还有更优的处理拆边的方法。因为这个条件是勾股数的条件。

而对于每组勾股数。均可以由以下方式构造

a=( m^ 2 - n ^ 2) * d,  b = (2 * m * n) * d, c  = (m ^ 2 + n ^ 2) * d

m> n > 0 , d > 0

其中m,n的奇偶性必须不同,并且gcd(m, n)= 1 

令s = a + b + c

即s= 2 * m * (m + n) * d

s/ 2= m * (m + n) * d

其中(m+ n)必然是奇数

令k= m + n 则m < k < 2 * m

现在就是枚举s/2的因子。。注意上限是sqrt(s/2)向上取整 -1 因为 m < m+ n

然后令m等于枚举的因子后, (m + n)* d就算出来了

然后就枚举k满足m < k< 2 *m 并且k<= s/ 2 / m中的所有奇数了

然后就算出来了。

 

标称用的时间是600ms,如果刚开始用一个二重循环枚举10000以内满足要求的勾股数的话,用的时间会在2s+,不过题目给的时限是10s。

这道题其实挺直接的,绝对比ds出的那几道搜索好写。但是估计都被前面的题卡太久?就没多少人做这个题了。


你可能感兴趣的:(ACdream群原创群赛(2) A~F J 解题报告)