做题记录3

答题

折半搜索,很妙的思路。发现很多题虽然题面上写着概率和期望,但其实并没有关系。n的范围是40,直接暴力搜索肯定会爆。所以可以把分成两半来搜索,复杂度\(O(2*2^{20})\),然后考虑如何把答案合并起来。我们要求的是第\(2^{n}*p\)的数是什么。可以二分来求,分别将两个部分排序,二分这个数是谁,一个指针在第一堆数从小到大扫,另一个指针在另一堆数从大到小扫,然后判断够不够这么多个数即可。

联合权值`改


考场上把题目读错了,以为一个点对的贡献只能算一次,然后就只得了20分。首先对于一个点,考虑以它为中转点会有多少贡献,贡献为和它相邻的点两两之间的权值乘积再乘以二。根据\((a+b)^2=a^2+2*a*b+b^2\),可以把和他相邻点的权值和的平方算出来再减去每个相邻点权值的平方。这样肯定会算多,需要减去三元环的贡献。有\(O(m\sqrt m)\)的方法算出。统计每个点的度数,把原来的无向图变成有向图,度数小的向度数大的连边,度数一样的让编号小的向编号大的连边,复杂度我不会证。这样一个三元环只会被访问一次,减去这三个点两两组合的贡献就行。至于求最大的联合权值,可以把每个点出点的权值从大到小排序,然后再去找,如果不是个环,就取max再跳出

你相信引力吗


很显然要用单调栈来做,维护一个单调不增的栈,对于一个新的元素,从第一个大于他的数一直到栈顶的这些数和他都可以满足条件。所以每加入一个数,弹栈一次,ans++,将数入栈,要是栈中有数比他大,ans++。如果是相同的数,需要记录这个数从第一次入栈到他一共出现了几次。将这个数入栈的时候,还要加上这个数之前出现的次数。还有一种情况就是最后的数和开始的数符合条件,在所有数都入栈一次后,判断栈中剩余的元素,top是不是>2,因为是一个单调不增的嘛,所以肯定可以和最一开始最大的作出贡献,每次top--,ans++

数列

exgcd。要求的是\(|x|+|y|\)最小 \(x=\frac{c}{g}x_0+k\frac{b}{g}\),\(y = \frac{c}{g}y_0-k\frac{a}{g}\),以k为横坐标,画出图像,可以发现当x最小时,和最小,所以让x取最小非负整数解,和最大负整数解,取个min即可

数对

线段树优化dp,需要按a+b来排序 \(a_i>b_j, b_i>a_j\)需要把i放在后面,\(a_i<b_j,b_i<b_j\)需要把i放在前面\(a_i<b_j,b_i>b_j\)或者\(a_i>b_j,b_i<b_j\)怎么放都无所谓了,因为这两个都可以选上。先考虑普通dp怎么写,f[i][j]表示前i个数,选的最大的a值为j(离散化后的),获得的最大权值是多少。首先第i个数可以不选f[i][j]=f[i-1][j],如果选f[i][max(j,a[i])]=max(f[i][max(j,a[i])],f[i-1][j]+w[i])。这个过程可以用线段树来优化,线段树的叶子节点的下标表示选的最大的a值,每个节点存的是这个节点管理的区间中的权值最大是多少。如果a[i]<b[i],f[i][a[i]]要从f[i-1][1[a[i]]中找一个最大的来转移,从线段树上查询1a[i]的最大值即可,而对于a[i]到b[i]这段区间,肯定是可以选上这个数的,因为选出来的最大的大于等于a[i],让这个区间都加上w[i],不能比b[i]大,因为根据题目要求,前面的a要比后面的b小,另一种情况是a[i]>
b[i],从1~b[i]里找出最大值转移,因为前面的a要比这个b要小,然后把这个点的f值更新到线段树的a[i]这个地方,取个max即可。最后输出根节点的最大值就行了

分组配对

肯定能想到从前往后一直取,一直到不能取了再去放下一组。但是没有想到怎么快速求出加入一个数答案的变化,然后就不会了。其实好像并没有好的办法快速求出来。可以换一种优化的方法,对于一个左端点,二分右端点,然后把区间排一个序,再扫一遍,判断是不是满足,不过这样复杂度会很大,可以换一种二分的方法,枚举一个长度为2的k次方,判断从左端点i到\(i+2^k-1\)是不是合法的,合法的话就k++,否则答案肯定在\(2^{k-1}\)\(2^k\)之间,再在这个区间内二分,这样复杂度会优秀很多。

城市游戏

首先dij一遍求出每个点到1的最短路径,再以n为起点dij一遍求出每个点到n的最短路径,定义dis[i]为从n到i的最短路,ans[i]为删掉最短路径树上i到他父亲的边,从n到i的最短路,D[i]为删掉一条边之后从i到n的最短距离。f[v]=min(max(f[cur]+edge[i].dis,tag[i]?ans[v]:dis[v])),如果之前已经删掉了一条边,那么只能走这一条边,如果没有删过,可以把这一条删了,如果这条边是最短路径树上的,那么距离就是ans[v]否则就是dis[v],但是我们不应该每次都取max而是找出来最小的f[cur]+edge[i].dis来,再和删除这条边的路径长度取个max,这样相当于小B截断了小A的最优路径,让小A取到次优路径。但是这样做是正确的,因为只有一次ans[v]>f[cur]+edge[i].dis,只有当v是cur在树上的父亲时才会取到ans[v]

adore

必须要把这个题解写了。每一层有两种决策,一种是将边反转,一种是不反,k很小,考虑状压,题目只要求到达终点的方案数为偶数的情况,所以可以用状态s来表示从起点到这一层的每一个点的方案数的奇偶性,1为奇数,0为偶数,可以把这一层的每个点与下一层的联通情况状压出来。如果说到这一个点\(p(i,k1)\)的方案数为奇数,那么和他相连的点\(p(i+1,x)\)的方案数也会是奇数,如果说这一层还有一个点\(p(i,k2)\)的方案数为奇数,并且和\(p(i+1,x)\)也连着一条边,那么到\(p(i+1,x)\)的方案数就会变成偶数。而如果是偶数的话,到下一个点的方案数的奇偶性并不会发生变化。可以这么dp,先枚举每一层,把这一层每个点和下一层的联通情况表示出来,一个是a[i],一个是b[i],分别是反转边和不反转边的联通情况,然后枚举这一层每个点方案数是奇还是偶,如果第i个点是奇,那就让Aa[i],Bb[i],然后让f[i][A]+=f[i-1][s],f[i][B]+=f[i-1][s]。到了最后一层,特殊处理。当倒数第二层有偶数个点的方案数为1时,才能累加到ans上

posted @ 2020-10-27 21:41  ghosh  阅读(3)  评论(0编辑  收藏  举报
莫挨老子!