题目不告诉乃们
T1 投票
题目理解1:输出占每个人投票总数的百分比,最终得分65分
题目理解2:输出占有效票的百分比,过不了样例
题目理解3: 输出占投票人的百分比,最终得分100分
考点:字符串的读入(尤其对于C++),双关键字快排和语文水平
期望得分100
T2 drawf tower
题目理解1:根据n个物品推测每件物品只能出现1次,直接递归一次求解,最终得分40
题目理解2:根据n个物品和两种获得方式推测每件物品最多可以从每种渠道获得两次,一次是合成,一次是购买
做法1:因此考虑树形DP 用f(i)表示每件物品最小值,用a(i)表示每件物品已经被哪一种渠道获得,若两种都已经出现则返回MAX,记忆化搜索更新,时间复杂度O(N+M).期望得分100,最终得分65
做法2:dfs暴力卡时,注意好卡的时间防止超时即可,时间复杂度T(70000000)期望得分40,实际得分60
题目理解3: 根据n种物品推测每种都可以无限次出现,因此考虑这是一个图,图中的每一点都可以从两个点一起到达,因此用队列进行更新即可,时间复杂度看具体实现,期望得分100,最终得分100
然而我们将图进行转化,通过一条边,权值设为另一条边的当前最小获得钱数,因此变成了从任意点到点1的最短路.SPFA即可时间复杂度O(kNM),因为k非常的小因此可以通过全点,期望得分100,最终得分100
T3 J语言
5分做法:直接输出每一个数的平方和。期望得分0,最终得分5
40分做法:枚举左端点和右端点,判断里面是否有括号,如果有递归求解重复上述做法,如果没有从右向左模拟计算表达式的值。
时间复杂度O(n^5*k)
60分做法,预处理出括号的位置O(1)判断暴力中是否出现括号,考虑从左向右递归整个表达式进行计算.时间复杂度O(n^2+n^3*k)
68分做法,发现阶数最多为10但是不容易维护整个表达式的次方值,考虑预处理出X的0到10次方以及每一阶的和,如同60分做法一样方案计算整个表达式,每个递归的子串中lazy计算子串表达式的值,时间复杂度O(n*10+k+lens+实际表达式是对于计算中是否先计算后阶乘的个数),期望的分60+,实际得分68
80分做法,在68分的基础上我们希望能够降低先计算后阶乘给整个计算带来的影响,因此我们想到:预处理出每一阶与在读入串中的数字的n次方的乘积,以及数字*n后的n次方的乘积
然后对于大部分对于向量的改变的式子都可以O(1)处理
实际复杂度O(n*40+k+lens+一个表达式对于不能O(1)处理的子串表达式的值)
期望得分80,实际得分未知【因为写报告的人太弱因此没能实现,80分做法全靠脑补
92分做法,因为阶数最多只有10,因此我们对于表达式分阶计算。
比如乘上常数就等于把每一阶的值都乘上常数,表达式相加等于每一阶相加,表达式相等等于每一阶的数乘上另一个数某一阶的数,放在进阶位中,与高精度加减乘类似,不需要进位直接取最后8位即可,然后预处理出向量每一阶的平方和,就可以把向量当成标量来计算
时间复杂度O(10*(n+10*lens)).期望得分100,实际得分92
例:求(X+3)^2
阶数 0 1 2 3 4 5..........
3 1
*
3 1
=
3*3 1*3+1*3 1
=
9 6 1
即为
X^0*9+X^1*6+X^2*1
预处理好X的次方和
于是上述式子可以T(100)求解
最多为T(100000*10+100000*100)=T(11000000)完全不会爆
100分做法: 上述做法时间明显可以承受,然而系统栈是十分小的,经测试会因为爆栈而只有92分.
扩大系统栈代码C++(Pascal未知,可以去bzoj的某一些题找到代码或者问度娘【度受):
#pragma comment(linker,"/STACK:102400000,102400000")
int size =256 << 20; // 256MB
char *p =(char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" ::"r"(p));
我才不会告诉你们NOIP2015D2T3我也是用相同的系统栈代码水过去的= =
对于本题把256改成128就可以过了
标程的系统栈代码:
int size = 1<<25;
char *p =(char *)malloc(size) + size;
__asm("movl %0,%%esp\n" :: "r"(p));
T4 图
并不会暴力,也许暴力枚举有多少个点然后生成边应该是1到3分这个样子
64分做法:
发现除去白色连白色,黑色连黑色的边后这是一个二分图
对于二分图明显无论从哪边数边,总边数总是相等的.
因此n/m=c/b............................................... 1式
在看看白色连白色,黑色连黑色的边
A*n/2为白色连白色的总边数
D*m/2为黑色连黑色的总边数
因此A*n和D*m都必须是偶数..................................2式
考虑大小的限制,为了足够连边,显然有
n>a,n>=c....................................................3式
m>d,m>=b....................................................4式
因此首先让
n=c/(gcd(b,c)),m=b/(gcd(b,c));
然后从1开始枚举一个t
让n*t和m*t都满足2,3,4式
n=c/(gcd(b,c))*t,m=b/(gcd(b,c))*t;即为第一问答案
考虑怎么生成边,用du[i]来表示这个点的入度,cx[i][j]表示i和j之间是否连边
然后枚举i
检查入度差多少
选择可以选择的范围中有最小入度的没有连过边的点连边即可
时间复杂度O((n+m)^3),n和m最大可以到2600+,因此期望得分70,实际得分64
92分做法
对于上述求最小值用priority_queue代替,就是用堆维护最小值
枚举i
检查入度差多少
将所有可以选择的点放入空堆中维护
每次get最小值连边,删去这个元素
维护入度出度和连过的边
时间复杂度O((n+m)^2log(n+m))因为常数太大因此对于数据19和数据20需要接近4s才能跑完
对于Pascal选手代码量太大,C++选手可写
100分做法
做法1:我们发现对于连边来说我们一定可以从序号小的边向序号大的入度最小的边连,这样不需要判重而且具有单调性
因此
把所有点和入度加入堆
枚举i
检查还需多少入度
Get最小入度,如果这个点在<=i则舍弃并继续get
连边
将连边的点的入度更新后重新放入堆中
结束枚举
清空堆
时间复杂度O((n+m)^2+log(n+m))
期望得分100,实际得分100
做法2:
当然这是std的做法= =
我们可以分成四类讨论
类1:对于白点,每一个其他相邻连续白点连出a/2条边,给对面黑点连边则每次选择从1开始计数器标记的黑点作为起点,连出b条边即可
类2:对于黑点,如同白点一样处理
类3:若a为奇数,我们在a/2那里少连了1条边,从前n/2个点向后n/2个点每个点连一条边即可
类4:若d为奇数,如同类3一样处理
时间复杂度按每一类相加O(n^2+n^2+n+n)
应该是接近这道题瓶颈的算法了
得分100
最后透露一下YMW的作死,语录如下:
那么点那么少,我开了1000的数组够啦!哎算了,2500吧,这够了吧,哎真的是,太简单了= =
实际情况如下:
什么?两个WA我就认了,怎么还会有两个崩溃?!!!!
然而猜到真相的我并没有告诉他
看到了好多熟悉的人,好吧大部分马上就会见面的
暴力膜里面各种神牛
BPMGG是改过之后的,BPM才是最开始评测的成绩