( ̄︶ ̄)↗ 涨姿势

来源:互联网 发布:正装衬衫 知乎 编辑:程序博客网 时间:2024/05/16 06:02

http://codeforces.com/gym/100956

ICPCCamp 2016 Day 6 - Spb SU and Spb AU Contest(Greedy Game-贪心)

题意:给你n个东西,每个东西有两个关键字a,b,第一个人按照关键字a的大小取,每次取a最大的,a相同情况下随意,然后第二个人最坏情况下保证能取到的东西的关键字b的总和是多少?

题解:这题开头想想应该是个贪心,但是不是很好贪,dp是反正做不了

可以先考虑第一个人怎么取,第一个人是一隔一的取,然后第二个人就是取了所有的偶数位,但是这样并不是最优的,但是第一个人对于每个位置n,必须取了大于等于一半的东西,所以可以把第二个人偶数位上取得,前面比较小的,去换后面奇数位比较大的值,然后这样换就能得到最优

不是很好表达,随意YY下

用优先队列维护呢。

AC代码:http://paste.ubuntu.net/15803999/

 

ICPCCamp 2016 Day 6 - Spb SU and Spb AU Contest(Sort It!-dp+树状数组+组合数学)

题意:题目挺烦的,让你求的东西就是这个排列里面有多少个上升子序列,然后只用这些上升自序列里面的值,去构造一个长度为n的串,一共有多少种方法

题解:求多少个上升自序列就是dp+树状数组咯,很容易,就是用这些值去构造n的串,就等价于用m个值,可以重复出现的去构造长度n的串,然后想了下应该是组合数学里的方法,各种递推都不太好使,于是发现应该用A[i]表示构造长度n的串,用i种值的方法,然后求A[i]=i^n-C[i][i-1]*A[i-1]-C[i][i-2]*A[i-2]-....-C[i][1]*A[1],先处理出来组合数,然后n^2的递推就能求出A[i]了。

然后每次dp之后,加上dp[i][j]*A[j]就行了

AC代码:http://paste.ubuntu.net/15804384/

还有个题明天再补把,也是姿势满满


http://codeforces.com/contest/611/problem/D  (dp+lcp预处理)

题意:给你一个长度为n的数字串,然后让你分割成几段,分割开得到的那些数字,是严格上升的并且没有前导0,问你有多少种分割的方法

题解:首先考虑分割,感觉是用dp,dp[i][j]表示结尾是第i位,然后切小于等于j个长度,然后这样切割的方法个数

然后你就往前切j个数字,然后再往前切j个数字,只要比较前面这j个组成的和后面这j个哪个大,如果后面的大就dp[i][j]+=dp[i-j][j],否则就是dp[i][j]+=dp[i-j][j-1]。

每次循环的时候dp[i][j]+=dp[i][j-1]。然后就是考虑前导0的细节了,如果i位或者i-j+1位或者i-2j+1位有0,需要分类考虑一下情况,然后如果i-2j+1<0的话,就要dp[i][j]+=dp[i-j][i-j+1],然后一些细节的地方注意下,这个转移就好叻、

然后就是前j位和后j位的比较大小了,如果直接比较的话,就是n^3的复杂度了,肯定不行,所以需要预处理,然后想到就是i和i-j这两个后缀的lcp,可以用后缀数组解决,但是后缀数组太麻烦了,加上串只有5000,所以可以使用n^2的递推啊,枚举i,j,if(s[i]==s[j]) lcp[i][j]=lcp[i+1][j+1]+1;

AC代码:http://paste.ubuntu.net/15810205/


http://acm.hdu.edu.cn/showproblem.php?pid=5289 (two pointer)

题意:给你n个数字,然后问你有多少个区间,区间里最大最小差小于k

题解:two pointer经典题,左右两端扫一遍,维护set维护里面的最大最小,map维护里面的每个数字出现过几次

AC代码:http://paste.ubuntu.net/15811094/


http://acm.hdu.edu.cn/showproblem.php?pid=5288 (求贡献,预处理+set二分)

题意:给你一个数组,然后里面有10W元素,大小1-10000,给你一个公式,公式的意思是,区间内不被其他数整除的数的个数,然后对所有区间的这个值求和

题解:想到元素的值才1W,就果断给1-10000的数字都打表,处理因子,然后给数字排序,记录每个元素在原来数组里面的坐标,然后所有的坐标都扔进对应的set里,比如1的坐标就扔进s[1]里。

然后考虑每个元素对答案的贡献把,就是考虑这个元素在多少个区间内,不被区间里的其他数字整除,那么就和单调栈类似,考虑左边和右边最远能到达的,都不是他的因子的距离,然后就是枚举他的因子,去对应的set里面找,然后找到他的坐标两边的左边,然后距离相乘求和。

为了方便计算,考虑每个元素的时候,先把他的坐标在对应的set里面删除,然后计算完毕之后再放回去,STL的运算有点多,要防止迭代器指向不对的地方。

AC代码:http://paste.ubuntu.net/15828708/


http://codeforces.com/contest/660/problem/E (组合数学+公式推导)

题意:给你一个长度n的数组a,由1-m的元素构成,问你所有a的里面的不同的子串(可以不连续)的和

题解:空串额外加,考虑长度为k的子串,为了防止重复考虑,所以只考虑这个子串在每个串里面第一次出现的位置,假设k个数字出现的位置是x1,x2....xk,于是1-x1的位置里面的值都不能和x1相同,所以都是m-1种方法,x2到xk之间都是这么考虑,然后xk后面,就随便取,每个位置都是m个取值。

于是就得到了公式

然后对这个公式进行化简,因为里面j每次都在变,不好对组合数求和,所以把两个求和符号换个位置,j在外面枚举,k在里面枚举,然后就能得到一个二项式定理的展开,得到

sigma (m^(j+1)*(2m-1)^(n-j-1)) ,0<=j<n

最后加上m^n就好了

我感觉这题还是很难的,主要是前面那个考虑的思想,如何不重复计数,是需要多学习的

AC代码:http://paste.ubuntu.net/15828989/

 

https://www.bnuoj.com/v3/contest_show.php?cid=7785#problem/B (线段树+瞎搞)
题意:给你一个排列,然后两种操作,一种是把l和r的位置交换,一种是求区间l到r的和,并且i位置的值是i位置的值作为那个位置的值。有点绕,然后求区间和
题解:肯定是线段树辣,然后线段树里维护的就是每个位置的值的那个位置的值,然后就是交换的时候需要更新
更新比较烦,首先是l,r两个位置,还有就是值作为位置是l和r的那两个位置的值,首先把l和r的值交换,然后pos[pos[l]]更新l位置,pos[pos[r]]更新r位置,然后就是另外两个点,另外两个点可能和l,r重合,所以需要考虑一个不会搞混他们的方法,首先是要用pre记录值i在哪个位置,然后pre[l],pre[r],就是值l和r所在的位置,首先我们需要更新pre[pos[l]]和pre[pos[r]]的位置,然后找值l和r的位置,然后把这两个位置更新成pos[l],pos[r],就行了,先后顺序一定要搞清楚,先更新pre,然后再单点更新。
感觉脑子一定要清醒啊。
AC代码:http://paste.ubuntu.net/15996530/


0 0
原创粉丝点击