1.总结:
这次考得有些不理想。原本打算拿到250分,结果只有180分。因为第三题的题目看错了,我以为是无向图,结果是有向图。结果第三题对拍也拍不出来,就爆0了。总的来说虽然不是很差,但也不是很满意。
2.题目分析:
1.弹钢琴:
这个题目如果乍一看,会想到用sort排序一遍再用排列组合来写。比较难以处理的是这个组合的大小。因为运用排列组合公式的时候,会有除法,所以不能提前模,这就会导致乘出来的数炸long long了。
一开始我想到的是利用算阶乘的方法把每一位的质数及其个数给求出来。但是这样第一个是代码较繁琐,第二个是要处理的数到后来会变得很大,而且你无法快速求出这个素数是否已经出现过,或者是在哪个位置上,光是处理这个的复杂度就有O(n)了,着实不算是一种好办法。
我太执着于求出其中某一个i的情况。虽然说中间想到了递推,但是却仍想到的是组合数的变换,多了一个数少了一个数,只局限于刚好取k个数,没有利用好其他如k-1,k-2以至于1的答案,可以拿这些来递推。其实这里类似于一个dp。而dp有一个非常鲜明的特点,就是只用考虑上次的情况,不需要再考虑在之前的情况。
如果想到的话,其实非常简单。考虑到这一次的k个数,因为要求第i个键盘按了几次,那么它必定会选,那么选到他的次数,就是前i-1个数取k-1个数的情况。接下来只要维护总和就好了,如果不取这个数,也要递推下去。所以代码非常短,主要是考这个递推式:
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
其实这个题目还有一个解法,再回到第一种解法,发现比较棘手的是组合有除法,不能提前模于是就会炸long long。那也就是说如果把除法换成等价的乘法,不需要答案一样,但是只要模的结果一样,就可以提前模了。如果没学过基本上不可能想到,但是由于前几天刚学到,我没有想到就有些不应该了。
说到这里,肯定有许多大佬已经知道了,那就是逆元。其实也简单,但是要先用到费马小定理:
假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)
那么对于任意的f(n)(f(n)≡0(mod x))f(n)/x%P≡f(n)*y%P (x*y≡1 (mod(P))
如果把x和y以及f(n)都转化为kP+t的状态,就可以证明这命题显然。所以除以一个数x就等于乘以x^(p-1)。这个P一般上都会给1e9+7,是个质数,再加上快速幂,就可以快速得出答案。
附上代码:
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
sort(val+1,val+n+1);
for(int i=1;i<=n;i++){
memset(res,0,sizeof(res));
res[1]=1;
for(int j=1;j<=k;j++)res[j]+=tot[j-1],res[j]%=P;
ans+=val[i]*res[k]%P;
ans%=P;
for(int j=1;j<=k;j++)tot[j]+=res[j]%P,tot[j]%=P;
}
cout<<ans%P<<endl;
return 0;
}
2.删除数字:
这个题目一开始想打暴力,后来尴尬发现暴力不会打,打了也只有30分左右。所以我观察了一下数据,发现这个题目比较适合dp,但如何定义。因为前几天刚写过状压dp,所以这题我就想到了状压,每一位的状态定义为0,1.即为取和不取。这样的话复杂度大概是O(n^2*m),只能过80分的数据。考试的时候我在想该如何优化,然后我发现前面几维枚举是不可能优化的,主要优化的是在比较两个状态是否满足单调。这个还好,只要状态确定了,那么这可以预处理出来,这个状态对应着什么数字。
但是这样还是不够,因为这样dp的转移复杂度还是很高,要把之前的状态都转移过来。这个我就没有想到比较有效的优化方法。主要的原因有两个:
一:如何把满足的数字放在一起处理:
二:就算放在一起,至少也要用O(1)的复杂度求出这么多数字的和。
我一开始想到用前缀和来处理,但是由于一个dp的状态改变了之后,那么后面的dp也要全部改变,复杂度依然讲不下去。针对第一个问题还有解决的办法,如果把dp状态改一改,这不再是每一位取和不取,而是对应一种从小到大的状态,有点类似Hash的想法。
由于前缀的不能实现,我也就放弃了AC的想法。其实我没有注意到dp的递推,其实是从上次的dp和前一个dp前缀和累加起来的。而又由于上次的dp已经无需改变,这次dp的递推从上次累下来,也就是O (1)的复杂度。
好,大体的思路有了,快速查找的话如果已经排序了的话,那么二分就可以过了。当然,这依然不是最好的方法。如何把这log(n)给优化掉呢。如果每一次都让两个数组一起往前循环,比较大小,就可以归并了。那么这样就不需要log(n)了。
3.四点旅行:
这个题还是算比较简单的题目了。一开始用Floyd求最短路,发现要是把四个点都枚举了的话,仅仅只有30分。但是如果枚举3维贪心1维,最后一位最大值可以预处理处来,需要注意的是要判重。这样的话就有60分了。
其实想到这里,和100分已经很接近了。一开始有种贪心就是从一个点出发,找到它的最远点,这样找4个。但显然是伪证。所以在想到枚举2维贪心2维,就产生了一些阻力。这是因为后面两维会被互相影响。
但如果想到枚举的是中间2维的话,那么旁边两维的唯一影响,就已经确定了。这个时候,就可以预处理出最大值,还有判重。
当然,这样求解的复杂度就下降了,但是求最短路还是Floyd,依旧是O(n^3)次方,显然不行。这里就有几个想法,第一个使用Bellman,第二个是Dijkstra算法。当然,如果是一条链的话,Bellman的复杂度就退化了,就会超时。如果数据不水的话,Bellman显然过不了。
因为我忽略了两个点的距离是1这个条件,如果利用好的话,就可以用bfs,复杂度也非常可观。
3.反思:
考试还是太过于粗心了,以及思维也太短了,有时候只差一步就可以得到正解了,遇到了许多情况应该罗列下来,写一写解决方法,也有便于理清思路。