[题解]noip2016普及组题解和心得
前言
感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来。至于我的分数嘛。。还是在你看完题解后写在[后记]里面。废话不多说,开始题解。
(其实这篇博客只有题解没有心得。)
第一题可以说的内容不是很多吧。直接暴力,计算每种铅笔需要花费的金额。
只不过计算的时候,需要注意如下问题
- 如果不是整数倍,除完后要加1
- 神奇的Linux系统,很多人的第三个点wa了,所以要养成良好的编写代码的习惯
Code
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<set>
#include<map>
#include<queue>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} int n;
int cost[], num[];
int result; inline void init(){
result = 0xfffffff;
readInteger(n);
for(int i = , b; i <= ; i++){
b = ;
readInteger(num[i]);
readInteger(cost[i]);
b = n / num[i];
if(n % num[i] != ) b += ;
smin(result, b * cost[i]);
}
printf("%d", result);
} int main(){
freopen("pencil.in", "r", stdin);
freopen("pencil.out", "w", stdout);
init();
return ;
}
第二题,暴力也不会TLE,所以就从起始日期枚举到结束日期,中途把进位之类的事情做好,问题就不会很大。
只不过仍然会存在一些问题
- 进位应该从低位开始判断
- 注意进位的边界
如果不小心手抽了,有些日期跳过了,或者有些地方出了点问题,变成了死循环,也很麻烦。
Code
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<set>
#include<map>
#include<queue>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} typedef class MyDate{
public:
int year;
int month;
int days;
MyDate(){}
MyDate(int num){
days = num % , num /= ;
month = num % , num /= ;
year = num;
}
boolean isHuiWen(){
if((year / ) != (days % )) return false;
if(((year / ) % ) != ((days / ) % )) return false;
if(((year / ) % ) != (month % )) return false;
if((year % ) != ((month / ) % )) return false;
return true;
}
void next(){
days++;
if(month == && days == && (year % != && (year % == || year % == ))){
days = ;
month++;
}else if(month == && days == && (!(year % != && (year % == || year % == )))){
days = ;
month++;
}else if(days == && (month == || month == || month == || month == || month == || month == || month == )){
days = ;
month++;
}else if(days == && (month == || month == || month == || month == )){
days = ;
month++;
}
if(month == ){
month = ;
year++;
}
}
boolean operator !=(MyDate another){
return !(this->days == another.days && this->month == another.month && this->year == another.year);
}
}MyDate; MyDate start;
MyDate _end; inline void init(){
int a, b;
readInteger(a);
readInteger(b);
start = MyDate(a);
_end = MyDate(b);
} inline void solve(){
int result = ;
while(start != _end){
if(start.isHuiWen()) result++;
start.next();
}
if(_end.isHuiWen()) result++;
printf("%d", result);
} int main(){
freopen("date.in", "r", stdin);
freopen("date.out", "w", stdout);
init();
solve();
return ;
}
[小结]
这道题的难度不是特别难,但是,有个很明显的问题代码不简洁,而且好像读入优化也没什必要。。
明明70几行可以打完的程序,我却偏偏打了100行。
如果是C语言用户,可以手打一些函数来代替我写的成员函数
第三题是暴力加优化。
首先讲思路吧。从头到尾扫描。需要一些量比如说上一艘船,对于它来说,在24小时内最早的一艘的位置(数组中)(就记为last吧)。还有当前不同种类的游客。还需要统计每个国籍的游客的数量(在这艘船的24小时内)
处理一艘船的时候,解决如下几个任务
- 更新对于已经不在24小时内的船,从last开始while循环,减去它们对应的国籍的游客数,再判断它是否为0,如果是,那么就把当前不同国籍的数量减去1
- 读入该艘船上的游客,如果这个国籍的数量为0,则把当前不同国籍的数量数加1,接着把这个国籍的游客数加1
- 输出这个不同国籍的数量
关于还省内存的事再说一下
- 使用vector
- 用两个一维数组,一个记录每艘船记录的数据在另一个数组中结束的下标,另一个就记录每艘船的游客的国籍
由于vector太慢(虽说对于这道题已经足够了),而且经常手抖导致崩掉,果断嫌弃,改用方法2(我也不是说vector一定不好,至少对于很多时候还是挺管用的,效率也没有我说的那么糟糕,只不过我不喜欢而已)
当然现在我很喜欢用vector偷懒 ——2018.2.13
Code
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} #define DAYSEC 86400 int n;
int tbegin;
int *times;
int listofx[];
int counter[];
int defkinds;
int *endsindex; inline void init(){
readInteger(n);
endsindex = new int[(const int)(n + )];
times = new int[(const int)(n + )];
tbegin = ;
endsindex[] = ;
for(int i = , peo; i <= n; i++){
readInteger(times[i]);
while(times[tbegin] <= times[i] - DAYSEC){
for(int j = endsindex[tbegin - ] + ; j <= endsindex[tbegin]; j++){
counter[listofx[j]]--;
if(counter[listofx[j]] <= ) defkinds--;
}
tbegin++;
}
readInteger(peo);
endsindex[i] = endsindex[i - ] + peo;
for(int j = endsindex[i - ] + ; j <= endsindex[i]; j++){
readInteger(listofx[j]);
if(counter[listofx[j]] == ) defkinds++;
counter[listofx[j]]++;
}
printf("%d\n", defkinds);
}
} int main(){
freopen("port.in", "r", stdin);
freopen("port.out", "w", stdout);
init();
return ;
}
首先呢,想一个正确但不高效的方法,枚举a,b,c,d然后判断是否存在,理论复杂度O(m4)。
接着可以发现一个物体的魔法值为2,还有一个魔法值为2,它们的贡献(实在找不到词了),是一样的。所以可以直接枚举数值,并且统计了数量后,可以O(1)判断d是否存在,复杂度降为O(4n3m)
看着4m有些不爽,决定把它优化掉。既然魔法值一样的物品贡献一样就没必要按照每件来记录作为A、B、C、D物品的次数,直接按照数值来记录,就没有必要去用第四重循环了。
计算的方法是这样的作为a物品的次数,根据乘法原理,就要加上可作为b物品、c物品和d物品的个数的乘积。没怎么说清楚,但是举个例子就好懂,比如说有1,5,24,26,26。当1作为a物品时,可作为b物品的是5,有一个,可作为c物品的是24,有一个,可作为d物品的是26,有两个,所以应该增加1*1*2次。其他同理。
为了对付极端数据(不过貌似没有),所以我拍了序,加了个lower_bound,然后并没有AC(滑稽)
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<ctime>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} int n, m;
int counter[];
vector<int> poses[];
int *fora, *forb, *forc, *ford;
int *mlist;
int *blist; int mylower_bound(int* a, int from, int end, int val){
int l = from, r = end - ;
while(l <= r){
int mid = (l + r) >> ;
if(val <= a[mid]) r = mid - ;
else l = mid + ;
}
return r + ;
} inline void init(){
readInteger(n);
readInteger(m);
fora = new int[(const int)(n + )];
forb = new int[(const int)(n + )];
forc = new int[(const int)(n + )];
ford = new int[(const int)(n + )];
mlist = new int[(const int)(m + )];
blist = new int[(const int)(m + )];
memset(fora, , sizeof(int) * (n + ));
memset(forb, , sizeof(int) * (n + ));
memset(forc, , sizeof(int) * (n + ));
memset(ford, , sizeof(int) * (n + ));
for(int i = , a; i <= m; i++){
readInteger(a);
counter[a]++;
poses[a].push_back(i);
mlist[i] = blist[i] = a;
}
} inline void solve(){
sort(mlist + , mlist + m + );
for(int i = ; i < n; i++){
if(!counter[i]) continue;
for(int j = i + ; j <= n; j += ){
if(!counter[j]) continue;
int k_start = mylower_bound(mlist, , m + , j + (j - i) * + );
if(k_start == m + ) continue;
for(int k = mlist[k_start]; k <= n; k++){
if(!counter[k]) continue;
int end = k + (j - i) / ;
if(end > n) break;
if(counter[end]){
fora[i] += counter[j] * counter[k] * counter[end];
forb[j] += counter[i] * counter[k] * counter[end];
forc[k] += counter[i] * counter[j] * counter[end];
ford[end] += counter[i] * counter[j] * counter[k];
}
}
}
}
for(int i = ; i <= m; i++){
printf("%d %d %d %d\n", fora[blist[i]], forb[blist[i]], forc[blist[i]], ford[blist[i]]);
}
} int main(){
freopen("magic.in", "r", stdin);
freopen("magic.out", "w", stdout);
init();
solve();
return ;
}
85分比赛时写的程序
正解呢就是很神奇的方法来做的,首先看张图
设c,d之间的差为i,则可以表示为上图。总长度大于9i。如果我们设这个长度是9i + 1,那么看,下图
a2 - a1 = 1。那么c1,d1可以和a1,b1组合,说明c2,d2也可以和a1,b1组合,也就是说,在计算c,d的时候,只需要累加a,b搭配的方案数,再按照乘法原理就可以求出来了。枚举的量也大大减少了,只需要枚举i和d,a。时间复杂度成功地降为可以接受的范围。
Code(看了正解后写出来的程序)
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<ctime>
using namespace std;
typedef bool boolean;
#define smin(a, b) (a) = min((a), (b))
#define smax(a, b) (a) = max((a), (b))
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
aFlag = -;
x = getchar();
}
for(u = x - ''; isdigit((x = getchar())); u = u * + x - '');
ungetc(x, stdin);
u *= aFlag;
} int n, m;
int counter[];
int *fora, *forb, *forc, *ford;
int *mlist;
int *blist; int mylower_bound(int* a, int from, int end, int val){
int l = from, r = end - ;
while(l <= r){
int mid = (l + r) >> ;
if(val <= a[mid]) r = mid - ;
else l = mid + ;
}
return r + ;
} inline void init(){
readInteger(n);
readInteger(m);
fora = new int[(const int)(n + )];
forb = new int[(const int)(n + )];
forc = new int[(const int)(n + )];
ford = new int[(const int)(n + )];
mlist = new int[(const int)(m + )];
blist = new int[(const int)(m + )];
memset(fora, , sizeof(int) * (n + ));
memset(forb, , sizeof(int) * (n + ));
memset(forc, , sizeof(int) * (n + ));
memset(ford, , sizeof(int) * (n + ));
for(int i = , a; i <= m; i++){
readInteger(a);
counter[a]++;
mlist[i] = blist[i] = a;
}
} inline void solve(){
// sort(mlist + 1, mlist + m + 1);
for(int i = ; i * < n; i++){
int len = * i + ;
int s = ;
for(int d = len + ; d <= n; d++){
s += counter[d - len] * counter[d - len + i * ];
ford[d] += s * counter[d - i];
forc[d - i] += s * counter[d];
}
s = ;
for(int a = n - len; a >= ; a--){
s += counter[a + len] * counter[a + len - i];
fora[a] += s * counter[a + i * ];
forb[a + i * ] += s * counter[a];
}
}
for(int i = ; i <= m; i++){
printf("%d %d %d %d\n", fora[blist[i]], forb[blist[i]], forc[blist[i]], ford[blist[i]]);
}
} int main(){
freopen("magic.in", "r", stdin);
freopen("magic.out", "w", stdout);
init();
solve();
return ;
}
(PS)
后话
从上面的描述中应该可以猜到我noip普及组的分数了吧。我也就不说了。
怎么说呢。。这也算是一个里程碑吧!虽然oi的路还长。。但是小小地庆祝一下也是可以的。
这次考得比较好的原因主要是心态比较好吧。也感谢四川吧,分数线比起什么浙江300分好多了(真的没有别的意思),所以说压力没那么大,而且靠差了还有提高组
[题解]noip2016普及组题解和心得的更多相关文章
- noip2016普及组题解和心得
前言 感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来.至于我的分数嘛..还是在你看完题解后写在[后记]里面.废话不多说,开始题解. (其实这篇博客只有题 ...
- noip2016普及组 题解
T1 大水题,不解释 上考场代码 #include <algorithm> #include <cstdio> using namespace std; int main() ...
- NOIP2008普及组题解
NOIP2008普及组题解 从我在其他站的博客直接搬过来的 posted @ 2016-04-16 01:11 然后我又搬回博客园了233333 posted @ 2016-06-05 19:19 T ...
- NOIP2016普及组解题报告
概述 \(NOIP2016\)普及组的前三题都比较简单,第四题也有很多的暴力分,相信参加了的各位\(OIer\)在\(2016\)年都取得了很好的成绩. 那么,我将会分析\(NOIP2016\)普及组 ...
- NOIP2002-2017普及组题解
虽然普及组一般都是暴力省一,但是有一些题目还是挺难的qwq个人觉得能进TG的题目会在前面打上'*' NOIP2002(clear) #include<bits/stdc++.h> usin ...
- NOIP2008普及组 题解 -SilverN
T1 ISBN号码 题目描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括9位数字.1位识别码和3位分隔符, 其规定格式如“x-xxx-xxxxx-x”,其中符号“-”就是分隔符( ...
- NOIP2016普及组复赛解题报告
提高组萌新,DAY1DAY2加起来骗分不到300,写写普及组的题目聊以自慰. (附:洛谷题目链接 T1:https://www.luogu.org/problem/show?pid=1909 T2:h ...
- 【做题记录】[NOIP2016 普及组] 魔法阵
P2119 魔法阵 2016年普及组T4 题意: 给定一系列元素 \(\{X_i\}\) ,求满足以下不等式的每一个元素作为 \(a,b,c,d\) 的出现次数 . \[\begin{cases}X_ ...
- NOIP2016普及组
普及组.代码有空发. 第一题就是买铅笔.暴力模拟绝对可取. 第二题就是回文日期.从t1的年份到t2的年份枚举每一年.头尾要特判. 第三题就是海港.骗了40分. 第四题就是魔法阵.不太好优化. 完.
随机推荐
- Axure 7.0 正式版 + 汉化包 安装
详情如下: Axure 7.0 正式版终于发布了,现在提供简体中文版给大家使用. Axure 7.0 正式版: 链接: http://pan.baidu.com/s/1kV4OJ47 提取密码: be ...
- 利用nodejs的cheerio抓取网站数据
/*引入模块*/ var http = require('http') var url = 'http://www.cnblogs.com/txxt' var cheerio = require('c ...
- NHibernate系列文章二十八:NHibernate Mapping之Auto Mapping(附程序下载)
摘要 上一篇文章介绍了Fluent NHibernate基础知识.但是,Fluent NHibernate提供了一种更方便的Mapping方法称为Auto Mapping.只需在代码中定义一些Conv ...
- Word自动生成目录
博主最近在写报告的时候要在Word里面做个目录,再做个页码,然后上网搜了一些方法,非常零散,我弄了好久才弄好.在这里我把整套方法分享一下. 声明:内容完全独创! 工具:Word 2016. 效果:如下 ...
- 获取View到顶部的高度
tv_title.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener ...
- ApexSQLLog总结
我们程序最近做更新,要做新功能的测试.程序经常出现大的事务,每次commit之后如果发现数据不对的问题也不能再回滚进行断点调试了,因为数据库数据已经更改了,所以一直想找一个回滚我数据库所有操作的工具. ...
- 让ztree树默认是关闭的
只需要在ztree的回调函数中加 var treeObj = $.fn.zTree.getZTreeObj("zTreeContent");treeObj.expandAll(tr ...
- 在线c++编译器(gcc)
这几年c++标准委员会活跃起来,C++11.14标准相续推出.对于想尝鲜又怕麻烦(visual studio 更新慢,对标准的支持力度也不够.对于使用gcc的,替换系统的gcc版本或者安装个mingw ...
- vim命令的妙用
进来看到了一篇博文,讲述的是无插件Vim编程技巧,然后看到里面还有一些其余的关于vim的博文,感觉值得收藏:酷 壳 – CoolShell.cn 这里挑选出其中一篇,这也是自己已经学习过的: 无插件V ...
- JavaEE知识点总结
JavaEE知识点总结 什么是分层开发? 一种化大为小,分而治之的软件开发方法. 分层的特点: 1.每一层都有自己的责任. 2.上一层不用关心下一层的实现细节,上一层通过下一层 提供的对外接口来使用其 ...