20170725考试总结

来源:互联网 发布:原生js 双向绑定 编辑:程序博客网 时间:2024/06/10 18:08

第一题:膜拜AZUI

题目描述

一天,小A给了J·G一道水题,J·G一眼秒了,现在J·G想考考你们:

AN个灯,排成了一列,现在小A给出来一个叫做azui的奇葩操作,我们把开着的灯看作数字1,把关着的灯看作数字0,定义0 azui 0 = 1,0 azui 1 = 0,1 azui 1 = 1, 1 azui 0 = 0。现在小AN个问题azui(l,r),表示询问从左往右的第l个灯向右一个一个azui到第r个灯的结果是什么。

输入

1行一个整数N表示序列的长度。

2N个整数Ai,每个数不是0就是1,表示灯是关的还是开的。

3行一个整数M表示询问的个数。

4~M+3行,每行两个整数lr,表示询问azui(l,r)

输出

M行,第i行回答第i个询问。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

51 0 1 0 152 33 44 51 31 4

样例输出

00001

提示

对于30%的数据,NM <= 5000


对于80%的数据,NM <= 500000


对于100%的数据,NM <= 1000000


题解:根据观察法azui运算是满足结合律和交换律,并且azui同一个数偶数次所得的值不变,所以只需要统计每一区间0和1的个数就可以算出答案。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=1000000+10;int T,n,l,r,a[N],now,sum[N];int azui(int x,bool flg){return (x==flg);}int main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}scanf("%d",&T);while(T--){scanf("%d %d",&l,&r);int tot1=sum[r]-sum[l];int tot2=r-l-tot1;now=a[l];if(tot1&1) now=azui(now,1);if(tot2&1) now=azui(now,0);printf("%d\n",now);}}

成绩:AC

分析:考试时还zz了一下,写了个树状数组认为自己很有道理的样子,然后考完发现自己想zz一样,明明不需要修改用个boll的树状数组O__O"…(幸好教师机跑得快让我水过去了~\(≧▽≦)/~啦啦啦)


第二题:小G的烦恼

题目描述

G最近郁闷死了,MZ想考考他的智商,给了他一道题,但是小G退役了这么久,怎么可能做的出来啊?于是他跑去向quack大神求助,可是quack大神要打牌,于是找到了你,希望能够不让MZ失望。

问题是这样的:MZ想去全球各地旅行。原本是有两家航空公司可以选择的,但是现在那两家公司合并了。然而,在合并初期,两家还没有交接好,于是出现了两家都要收钱的问题。由于MZ只想出去玩一个月,她可以选择包月机票(两家公司都有),对于其中的一家公司A来说,只要花费X元,即可以在持有另一家公司的合法包月机票的情况下,乘坐任何票价不高于A公司定价的X元的飞机。另一家公司也是一样的。

简单来说,对于航线i2种价格PiQi,分别是A公司和B公司的定价。假设你持有X元的A公司月票和Y元的B公司月票,当Pi<=X并且Qi<=Y时,你才可以乘坐航线i。当然,如果单独购买这一趟航班的两张票也是可以的。

现在MZ告诉你了N-1个她想要去的城市,MZ初始时在1号位置,并且告诉了你所有航线的两个价格。想要知道,最小的花费。

输入

12个整数NM分别表示城市的个数,航线的条数。

2-M+1行,每行4个整数uiviPiQi,分别表示航线的两个城市(飞机可以来回开),和两种价格。(可能存在重边和自环)

输出

共一行,一个整数ANS,表示最小的花费。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

5 51 2 3 21 3 2 42 4 4 25 3 3 31 4 0 1

样例输出

7

提示



选择除了2-4的所有边,所以答案是3+4=7


【数据范围】


对于20%的数据N,M <= 15


对于50%的数据N <= 200, M <= 500


对于80%的数据N <= 500, M <= 1000


对于100%的数据N <= 2000, M <= 5000, Pi, Qi <= 10^9


题解:首先单独买票一定不最优(一定要坐这趟飞机用同样的价格买月票更优)。标程的解法很。。。。假设递增枚举p,那么q一定单调不上升。(很显然但一般谁会想( ⊙ o ⊙ ))所以递增枚举p,递减枚举q,check图是否联通,因为每一个p和q都只被枚举了一次,见图和dfs时间为n+m,所以总时间为2*m*(n+m)。个人的暴力死解:先按p排序,枚举q上限,q一确定就可以用最小生成树kruskal找到p的上限(先按p排好,每次算时就不用重复排了),时间复杂度为m*m(好像更快的样子)

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<ctime>using namespace std;const int N=2000+10;const int M=5000+10;const int inf=0x3f3f3f3f;struct node{int u,v,p,q;bool operator < (const node &a)const{return p<a.p;}}arr[M];int n,m,Q,fa[N];int root(int x){if(!fa[x]) return x;else return fa[x]=root(fa[x]);}void merge(int a,int b){fa[root(b)]=root(a);}int cal(){int res=-1,tot=0;memset(fa,0,sizeof fa);for(int i=1;i<=m&&tot<n;i++)if(arr[i].q<=Q){if(root(arr[i].u)!=root(arr[i].v)){merge(arr[i].u,arr[i].v);res=arr[i].p,tot++;}}if(res<0||tot<n-1) res=inf;return res;}int main(){scanf("%d %d",&n,&m);for(int i=1;i<=m;i++){scanf("%d %d",&arr[i].u,&arr[i].v);scanf("%d %d",&arr[i].p,&arr[i].q);}sort(arr+1,arr+m+1);int ans=inf;for(int i=1;i<=m;i++){Q=arr[i].q;ans=min(ans,Q+cal());}printf("%d\n",ans);}

成绩:80

分析:(⊙o⊙)…,考试时的思路是枚举p二分q时间复杂度正好卡在1s左右(然后成功被卡了下来)。


第三题:小Q的新玩具

题目描述

期末考试完了,小Q得到了一件套新玩具,总共有N个零件。

现在小Q想把新玩具搬回家里,可是他遇到了新的问题:

每个零件有自己的重量Wi,小Q要租车把它们带回家。车每次只能运总重量和小于Lim的玩具,按照其中最重的玩具的重量收费。

零件不能拆分成更小的部分。为了不打乱零件的顺序,增加自己拼装的难度,每次装车只能装连续的部分。

现在想请你帮助小Q计算把玩具全部搬回家的最小费用。

输入

第一行两个整数N和Limit。 

接下来的N行,每行一个整数,代表第i个零件的重量。

输出

第一行一个数字,表示答案。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

8 17 2 2 2 8 1 8 2 1

样例输出

12

提示

N<=300000


Wi小于int范围


题解:很明显的dp,方程式很白痴,dp[i]=min(dp[j]+max(j+1,j+2,....i))。如果提前处理max,那么是一个n^2的转移o(>﹏<)o,只有30分(hyh大佬暴力60属于特殊情况),那么需要dp优化,首先用单调队列维护递减的a[i]值,max的值就可以确定了,用set保存i之前的dp[j]+max,因为dp值一定不递减。当来了一个新的i时将a[i]插入单调队列,被弹出的元素set中对应的失效,而当前dp为set中的最小值或满足limit的情况下尽量靠前的dp值加上对列头。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<set>using namespace std;const int N=300000+10;const int inf=0x3f3f3f3f;struct node{long long val,num;int pos;node(){}node(long long a,long long b,int c){val=a,num=b,pos=c;}}q[N<<1];int n,head,tail;long long a[N],sum[N],L[N],dp[N],Limit;set<long long> Ans;int main(){scanf("%d %lld",&n,&Limit);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}for(int i=1;i<=n;i++){while(head<tail&&q[tail-1].num<=a[i])Ans.erase(q[tail-1].val),tail--;while(head<tail&&sum[i]-sum[q[head].pos]>Limit)Ans.erase(q[head+1].val),head++;if(head<tail){int val=dp[q[tail-1].pos]+a[i];q[tail++]=node(val,a[i],i);Ans.insert(val);}else q[tail++]=node(0,a[i],i);int best=lower_bound(sum,sum+n+1,sum[i]-Limit)-sum;dp[i]=dp[best]+q[head].num;if(!Ans.empty())dp[i]=min(dp[i],*Ans.begin());}printf("%lld\n",dp[n]);}

成绩:30

分析:标准暴力分/(ㄒoㄒ)/~~,考试时考虑过用线段树维护变化的dp[j]+max(新来一个a把前面的值更新。。)然后太复杂没有调出来%>_<%。


总结:(⊙o⊙)…,这次考得很正常,简单的分都拿到了,有一点难度的全丢了(尴尬),感觉dp优化很重要,连续3场作为第三题压轴,第二题生成树的思路其实很简单(然而考场上没想到,考完恍然大悟。。。。)第一题理解题意时花的时间有点长。。。QnQ



原创粉丝点击