NOIP模拟2017.9.19 总结+心得
来源:互联网 发布:免费网络推广渠道 编辑:程序博客网 时间:2024/05/16 03:32
世界真的很大
今天真的亏,亏大了,眼睁睁放弃正解。。
恍然大悟时为时已晚
看题先:
1.NYG的背包
【问题描述】
扎扙扇有一个神奇的背包,每放进去一个物品,背包的体积就会变大。
也就是说,每放进一个物品,背包会被占用一定的体积,但是紧接着背包的总体积又
会增大一定的值(注意是在放入物品后背包总体积才增大)。
扎扙扇发觉这个背包十分好用,于是不由自主地想到了一个问题。
现在给出背包初始容量V以及n个物品,每一个物品两个值a; b,分别表示物品所占体积
和放入背包后背包增大的体积。
扎扙扇想知道能否把所有物品装进去?
因为扎扙扇比较老实,这么简单的问题自然装作不会做的样子。
于是他来请教你。
【输入格式】
从文件扢扡扣扫扰扡扣扫戮扩扮中读入数据
输入文件包含多组数据。
第一行一个数扔表示数据组数。
对于每组数据,第一行两个数n; h,接下来扮行每行两个数a; b表示物品所占体积和放入
背包后背包增大的体积。
【输出格式】
输出到文件扢扡扣扫扰扡扣扫戮扯扵扴中
对于每一组数据,如果能把所有物品装进去,输出戢扙扥扳戢,否则输出戢扎扯戢(不包含引
号)
这道题一眼就是贪心,看一下数据范围,每一次大概就只支持一次sort的时间,更加确定我贪心的想法
然后仔细一想,这个V好像是没什么用的。
为什么这么说呢?是因为对于一堆给定的物品,我们都是按照最优策略去放,就是说最优策略是一定的,不管V是多少,我们都是按照固定的顺序去放东西,V的大小只是决定策略能不能成功而已
然后我们就只需要决定一下对于给定的物品里面,放入的顺序
考虑比较两个物品谁先放的时候,如果两个都是使得放入之后增加的,那么肯定是让a小的先放。同理都是放入后使得空间变小的,肯定是让b大的先放,那一个增加一个减少肯定是让增加的先放
完整代码:
#include<stdio.h>#include<iostream>#include<algorithm>using namespace std;typedef long long dnt;struct obj{ dnt x,y;}a[1000010];int n,T;dnt w;bool cmp(const obj &a , const obj &b){ if(a.y-a.x>=0 && b.y-b.x>=0) return a.x<b.x; if(a.y-a.x<0 && b.y-b.x<0) return a.y>b.y; return a.y-a.x>=0;}int main(){ freopen("backpack.in","r",stdin); freopen("backpack.out","w",stdout); scanf("%d",&T); while(T--) { cin >> n >> w; for(int i=1;i<=n;i++) scanf("%I64d%I64d",&a[i].x,&a[i].y); sort(a+1,a+n+1,cmp); int flag=1; for(int i=1;i<=n;i++) { if(a[i].x>w) { flag=0; break ; } w-=a[i].x,w+=a[i].y; } if(flag) printf("Yes\n"); else printf("No\n"); } return 0;}/*13 53 14 88 3*//*37 926921366 12337178 2315516679 2372915062 28427939 678224224 930622778 136065 2236717444 544216452 3023614893 2422031511 136344380 294227 1870025935 458924962 957126897 1498220822 238021103 1264832006 2291223367 20674*/
2.NYG的动态数点
【问题描述】
扎扙扇是一个善于思考的好学生。
于是扎扙扇想往他的背包中放序列。
某一天扎扙扇得到了一个长度为n的序列faig。然后扎扙扇说,如果对于一段区间扛L;R扝,
存在L k R使得8i 2 扛L;R扝都有akjai,我们认为它有价值,价值为R �� L(若不满足条
件则没有价值)。
现在扎扙扇想知道所有区间中价值最大为多少,最大价值的区间有多少个,以及这些区
间分别是什么。
【输入格式】
从文件扰扯扩扮扴戮扩扮中读入数据
第一行一个整数n。
第二行n个整数ai。
【输出格式】
输出到文件扰扯扩扮扴戮扯扵扴中
第一行两个整数,扮扵扭和扶扡扬,分别表示价值最大的区间的个数以及最大价值。
第二行扮扵扭个整数,按升序输出每一个最大价值区间的扌。
这道题。。真的是血亏
当时想了一会儿没想出来。。就先写了一个n^3的大暴力,暴力枚举区间,然后直接check一下区间里德gcd在区间里存不存在
然后发现区间gcd可以用线段树得到,区间值域存在与否可以用主席数得到,由于gcd本身是log的,这样O(n)check就变成了log^2,总复杂度就是nlog^2,60分到手
60代码:
#include<stdio.h>#include<vector>#include<algorithm>using namespace std;int gcd(int a,int b){ return b==0 ? a : gcd(b,a%b);}struct Node{ int gd; Node *ls,*rs; void update() { gd=gcd(ls->gd,rs->gd); }}pol[1000010],*tait=pol,*root1;struct node{ int sum; node *ls,*rs;}pool[1000010],*tail=pool,*root[300010],*null;vector <int> G;int n,a[300010],ans=0,bns,flag=0;node* newnode(){ node *nd=++tail; nd->ls=nd->rs=null; nd->sum=0; return nd;}Node *build(int lf,int rg){ Node *nd=++tait; if(lf==rg) { nd->gd=a[lf]; nd->ls=nd->rs=0; return nd; } int mid=(lf+rg)>>1; nd->ls=build(lf,mid); nd->rs=build(mid+1,rg); nd->update(); return nd;}int query(Node *nd,int lf,int rg,int L,int R){ if(L<=lf && rg<=R) return nd->gd; int mid=(lf+rg)>>1,rt=0,lt=0; if(L<=mid) lt=query(nd->ls,lf,mid,L,R); if(R>mid) rt=query(nd->rs,mid+1,rg,L,R); if(!rt) return lt; if(!lt) return rt; return gcd(lt,rt);}void insert(node *ne,node *&nd,int lf,int rg,int pos){ nd=newnode(); nd->sum=ne->sum+1; if(lf==rg) return ; int mid=(lf+rg)>>1; nd->ls=ne->ls,nd->rs=ne->rs; if(pos<=mid) insert(ne->ls,nd->ls,lf,mid,pos); else insert(ne->rs,nd->rs,mid+1,rg,pos);}int query(node *ne,node *nd,int lf,int rg,int K){ if(lf==rg) return nd->sum-ne->sum; int mid=(lf+rg)>>1; if(K<=mid) return query(ne->ls,nd->ls,lf,mid,K); else return query(ne->rs,nd->rs,mid+1,rg,K);}int main(){ freopen("point.in","r",stdin); freopen("point.out","w",stdout); null=++tail; null->sum=0,null->ls=null->rs=null; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); root1=build(1,n); root[0]=newnode(); for(int i=1;i<=n;i++) root[i]=newnode(); for(int i=1;i<=n;i++) insert(root[i-1],root[i],1,3000,a[i]); for(int i=n;i>=1;i--) { if(flag) break ; for(int j=1;j<=n-i+1;j++) { int L=j,R=j+i-1; int tmp=query(root1,1,n,L,R); if(query(root[L-1],root[R],1,3000,tmp)) flag=1,ans++,bns=i-1,G.push_back(L); } } printf("%d %d\n",ans,bns); for(int i=0;i<G.size();i++) printf("%d ",G[i]); return 0;}/*3015 15 3 30 9 30 27 11 5 15 20 10 25 20 30 15 30 15 25 5 10 20 7 7 16 2 7 7 28 7 */
然后想了一下,好像可以直接枚举ak,然后往左右两边看最远的区间在哪里。这样的复杂度是O(n^2)的,也是60分,虽然比我的优,但得分也没什么差别,然后就放弃了。放弃了。。放弃了。。。
考完以后,琢磨怎么办,想了一下决定先把暴力的优化写出来,写完了以后,测样例,发现一个L可能被统计多次,写了个unique然后CE。。忽然发现如果被更新过的点自己再更新绝对没有之前优,可以跳过去,这样就可以避免重复统计的情况
然后A了。。。
想了一下,这样的时间复杂度主要集中在更新答案的地方,而只要这样一改,每个点只会被更新两次,时间复杂度O(2n)。。
完整代码:
#include<stdio.h>#include<vector>#include<algorithm>using namespace std;vector <int> G;int n,a[1000010],maxn=0;int main(){ freopen("point.in","r",stdin); freopen("point.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { int tmp=0,L=i,R=i; for(int j=i-1;j>=1;j--) if(a[j]%a[i]==0) tmp++,L=j; else break ; for(int j=i+1;j<=n;j++) if(a[j]%a[i]==0) tmp++,R=j; else break ; if(tmp==maxn) G.push_back(L); else if(tmp>maxn) { G.clear(); maxn=tmp; G.push_back(L); } i=R; } sort(G.begin(),G.end()); int cnt=G.size(); printf("%d %d\n",cnt,maxn); for(int i=0;i<cnt;i++) printf("%d ",G[i]); return 0;}
嗯,就是这样
- NOIP模拟2017.9.19 总结+心得
- NOIP模拟赛2017.9.11 考试心得+总结
- NOIP 2017.9.17 总结+心得
- 2017.9.17 noip模拟赛 总结
- 2017.9.17 胡策题 【题解 + 总结】【NOIP模拟】
- 2017.9.20 noip模拟赛 总结
- 2017.9.26 noip模拟赛 总结
- Noip模拟总结
- NOIP模拟8.12总结
- NOIP模拟20150904总结
- NOIP模拟9.19总结
- NOIP模拟10.27总结
- 10.28NOIP模拟总结
- NOIP模拟10.28总结
- NOIP模拟10.29总结
- 10.29NOIP模拟总结
- noip 11.5模拟总结
- 9.4NOIP模拟总结
- 【@CrossOrign】注解方式解决跨域问题
- Codeforces 847B Preparing for Merge Sort
- 如何从本地磁盘中读取一个文件,进行判断每个字符有多少个
- 解决org.apache.shiro.session.UnknownSessionException: There is no session with id的问题(转)
- php 常用函数
- NOIP模拟2017.9.19 总结+心得
- 机器学习第1章 : 目录
- jQuery-基本过滤选择器
- 2017-09-19 LeetCode_030 Substring with Concatenation of All Words
- 位运算学习笔记
- Unity Android平台下插件/SDK开发通用流程
- C#源码刷新网页 最小化托盘http get和post请求配置保存版权时间限制定时调用 单实例运行,如果已经运行则激活窗口到最前显示
- UDP使用connect系统调用
- SDK之动态链接库开发—基本概念