今天算是省冬的第一天……早上柯黑出题,说是“信心欢乐赛”,其实是“使你失去信心、不再欢乐的比赛”
顺便orz一下来看这篇文章的各路神犇……求轻虐
水题
(py.cpp/c/pas)
【题目描述】
小呆同学非常的懒,做数学题的时候喜欢用Python 做计算器,写一个脚本,用Python运行一下,把答案写到书上。
2017 年的一天,小呆遇到了一道很难的数学题,有上千个计算步骤,他辛辛苦苦地把需要的Python 脚本写完。但是悲剧发生了,他的Python 解释器坏了,而且由于“特殊”的访问限制,他无法登上www.python.org 重新下载Python 解释器所以他决定自己写一个简单的Python 解释器以满足他的做题要求,但是他没有时间,他还要去做其他的题目,所以他把编写Python 解释器这个艰巨的任务交给了你,一个未来的THUer。
你被要求实现Python 解释器中两种简单的功能:
语句类型格式解释
赋值语句A = B
把B 的值赋给A,这里的B 是一个合法的Python 表达式。
for example:
>>> a = 1 //a=1
>>> b = a + 100 //b=101
输出语句print(A1[;A2; :::])
输出print 后的每一个变量或者常量的值,用空格隔开,整个语句处理完成后输出一个换行符。
for example:
>>> a = 554 //a=554
>>> print(a; 11224)
554 11224
为了降低本问题的难度,特别约定:
• 变量名由5 8 个小写字符构成(’a’-’z’)。
• 表达式中只会出现+-*/ 四种运算符(不存在括号,除法运算符为整除并且使用/ / 表示)。
• 在Python 语言中默认是支持大整数运算的,为了简化问题,保证所有的常量都不大于2^15-1,但不保证输出结果以及中间过程的值不大于2^15-1,即,你有可能需要高精度计算
• 保证出现在赋值语句等号右边的变量以及在输出语句中出现的变量都被赋值过。
• 中间过程及答案可能出现负数。
【输入格式】
从py.in 中输入数据
保证输入文件是一个合法的Python 脚本文件,可以直接用Python3.X 解释运行
【输出格式】
输出到py.out 中
按照题目要求输出
【样例输入】
ctxbve=89+5+29-38
bhzocwv=41-ctxbve-73+10-81+78-ctxbve
kgthkrh=51-bhzocwv+bhzocwv-bhzocwv-ctxbve+41-ctxbve+23
ldvxco=kgthkrh+bhzocwv+kgthkrh-bhzocwv-33-ctxbve-59+57-61
print(bhzocwv)
lmohn=kgthkrh
print(12,77,kgthkrh,kgthkrh,lmohn,kgthkrh,ldvxco,16,29,lmohn,ldvxco)
print(bhzocwv,ldvxco,ctxbve,ldvxco,kgthkrh,kgthkrh,ldvxco)
【样例输出】
-195
12 77 140 140 140 140 99 16 29 140 99
-195 99 85 99 140 140 99
【数据规模与约定】
输入文件长度不超过10000 行。
40% 的数据保证不需要高精度计算。
第一题是神模拟+超级细节题……不仅要手写栈去搞加减乘除的优先级、哈希搞字符串、高精加减乘除,而且还有很坑爹的负数的情况。最重要的是,python运算机制和c++不一样。-3//2在正常计算中是-1,但是在python里-3//2=-2。结果全场爆0了……我承认我对它一点想法都没有……没有7、8千b的代码怎么能搞定……
简单题
(xor.cpp/c/pas)
【题目描述】
小呆开始研究集合论了,他提出了关于一个数集四个问题:
1. 子集的异或和的算术和。
2. 子集的异或和的异或和。
3. 子集的算术和的算术和。
4. 子集的算术和的异或和。
目前为止,小呆已经解决了前三个问题,还剩下最后一个问题还没有解决,他决定把
这个问题交给你,未来的集训队队员来实现。
【输入格式】
从xor.in 中输入数据
第一行,一个整数n。
第二行,n 个正整数,表示a1; a2; :::; an
【输出格式】
输出到xor.out 中
一行,包含一个整数,表示所有子集和的异或和。
【样例输入】
2
1 3
【样例输出】
6
【样例解释】
6 = 1
3
(1 + 3)
【数据规模与约定】
数据分为A,B,C 三类。
A 类数据(20%) 保证:ai > 0,1<=n<=10。
B 类数据(40%) 保证:ai > 0,1<=n<=1000,Σai<=10000。
C 类数据(40%) 保证:ai > 0,1<=n<=1000,Σai<=2000000。
另外,不保证集合中的数满足互异性,即有可能出现ai = aj 且i ̸= j。
这题还是可做的……
首先A类数据就是2^n暴力枚举所有子集,然后算异或和。这样只有20分
B类也比较容易,首先注意到一个显而易见的结论:对于任意x>=0,有x^x=0。
那么显然子集中两两相等的可以约掉。
那么考虑Σai<=10000,想到背包dp还是比较容易的
f[i][j]表示前i个数中凑出的子集的和为j的方案数。那么显然f[i][j]=f[i-1][j]+f[i-1][j-a[i]]
最后计算答案时就是枚举前n个数(就是所有数)组成的子集和,以及这个子集和出现的次数。如果出现偶数次,那么可以两两完全消掉。如果出现奇数次,那就不行啦……
空间上还可以优化成O(Σai)的。就是f[j]=f[j]+f[j-a[i]]
这样就是我写的60分。代码如下:
#include<cstdio> inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,sum,ans; int f[10010]; int a[1010]; int main() { n=read(); for (int i=1;i<=n;i++) a[i]=read(),sum+=a[i]; f[0]=1; for (int i=1;i<=n;i++) for (int j=sum;j>=a[i];j--) f[j]=f[j]+f[j-a[i]]; for (int i=1;i<=sum;i++) ans^=(f[i]%2==1)*i; printf("%d",ans); }
但是C类数据的Σai是200w级别的,空间上还没有问题,但是时间上O(n*Σai)的20e效率是无法接受的。
想一想B类数据的做法还有哪里可以优化的:
首先f数组对我们有用的并不是整个子集和出现的次数,而是它的奇偶性。因为这个和是否更新答案只和它是奇是偶有关,而与它具体出现次数无关
所以可以考虑用位运算来代替dp的数字更新。
把f压成200w的01串,我们可以考虑每50位用一个long long存0/1状态,这样优化到f[i],i<=200w/50=4w。
而递推就是从前面的i-a[i]转移。
有一种更好的方法是用c++自带的bitset,相当于一个200w位的2进制。然后可以整段的异或。这样相当于每次for i……之后的枚举是O(1)的。
#include<cstdio> #include<bitset> using std::bitset; bitset <2000001> f; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,sum,ans; int a[1010]; int main() { n=read(); for (int i=1;i<=n;i++) a[i]=read(),sum+=a[i]; f[0]=1; for (int i=1;i<=n;i++) f=f^(f<<a[i]); for (int i=0;i<=sum;i++) if (f[i])ans^=i; printf("%d",ans); }看起来bitset还是挺好用的。
我觉得这题是整套题中唯一可做题。
信心题
(cover.cpp/c/pas)
【题目描述】
在二维平面上有若干个多边形,每个多边形都覆盖了一定的区域,它们之间有可能重叠,请求出这些多边形遮住了多大的平面区域。即,求多边形的面积并。
【输入格式】
本题为提交答案题,共有10 个输入,分别是cover1:in cover10:in。
每个文件第一行,一个整数,表示这个输入文件的序号(1 10)。
接下来一行,一个整数n,表示这组数据中有n 个多边形。
接下来n 行,每行第一个整数Pi,表示这个多边形有Pi 个点,接下来有Pi 组整数,表示这个多边形的Pi 个点的坐标。
【输出格式】
请编写一个程序,根据提供的文件序号输出相应的答案(保留5 位小数)。
【样例输入】
0
1
3 0 0 1 0 1 1
【样例输出】
0.50000
【评分标准】
答案与标准答案偏差不超过2% 的可以获得全部的分数。
答案与标准答案偏差超过20% 的不得分。
其余情况根据下列公式进行评分:
【数据规模与约定】
多边形的点的横纵坐标的绝对值 10^8。
多边形数量不超过10^6。
这是我做过的第一道提交答案题。求多边形面积并。考场上看到100w个多边形实在没思路。后来我发现原来大家做这题都是看数据来想解法的。而我毕竟是太弱了,所以我一直在想一般解法,其实能过就是爷……那些解法都是看数据然后自己手推+yy一下想的。
这里没法把200+M的数据发上来,有点遗憾,因为不看数据自己大讲特讲就没有感觉了
第1、2测试点是只有一个多边形的情况。那么如果这是凸多边形直接凸包搞一下,然后围着一个点切成n-2个三角形,再用叉积算面积最后加在一起。但是出题人心态不好,题目描述上说是x、y<=10^8,但是实际上有x,y<=10^12的数据。这样算叉积的时候有24位long long会爆。所以中间过程要用dobule。
然而一中李泽龙大神指出有可能是凹多边形,所以要三角剖分……我不会QAQ
第3、4测试点有几个多边形,但是两两之间不相交,所以做法同上就可以
第5、6测试点有点坑……好像他们都是这里错了,因为前面的数据都是规则的长方形,但是出题人心态又不好了,在中间加了几个小正方形包含在前面的大正方形里面,这样依然没有交点,但是里面的面积显然是不能算的。但是李泽龙因为误差小于2%还A了……rp太高能说什么呢
后面差不多都是长方形+有相交的情况了(因为没仔细看数据)这用线段树搞矩形面积并就没问题了。
总之这些都是我在考完之后才会的。所以我第三题爆蛋了
总之我第一天就只有60分
其实分析一下主要还是经验不够。在考场上打暴力经验不足,对于提交答案题应该根据数据有的放矢,这样才有做出来的希望。
另外,提交答案题的暴力好像要一开始就去打,这样让它多跑一会儿……