百度之星2016初赛第二场(Astar Round 2B)
2016-05-26 16:27
405 查看
这场比赛难度比起第一场有所提升.
依旧按照我做题的顺序给出题解.
题目链接
很多题解都说可以打表找出规律之后O(1)用组合数计算,奥妙重重呀.
具体怎么实现,可以用Splay来维护,但还有一种更机智的用堆的做法,先将区间双关键字排序,考虑当前区间的L作为最终区间的交的L,从前面的区间中取出第k−1大的R值,这可以用一个小顶堆维护k−1个最大值来实现.注意k=1的情况.
这样做的复杂度是O(nlgn).
题目中很关键的条件是数据随机,而这种题目我几乎没有做过…连个水分的方法都没有想出来…
赛后看到很多人的做法都是找到区间最大值,更新答案后递归到两个子区间,这样做期望是O(nlgn)的,实现并没有什么技术含量.
ShinFeb有并查集+线段树的O(nlgn)玄学做法.
然而我并不会正方形求交….>_<
好吧其实这已经几乎是正解了.
设二分的答案为len,那么对每个R−L>len的区间[L,R],需满足|L−x|+|R−y|⩽len,这等价于
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪L−x+R−y⩽lenL−x−R+y⩽len−L+x+R−y⩽len−L+x−R+y⩽len
然后化为y关于x的四条不等式,这下判定有无解就方便了.
然而这里我懒得画图了…>_<
把所有满足条件的方案看成平面上的点,横坐标为选出的bi之和,纵坐标为选出的ci之和.
因为要使横纵坐标的乘积最小,所以最优解肯定在所谓的”下凸壳”上.
现在考虑如果已知满足条件的下凸壳上的两个点L,R,如何求一个横坐标在两者之间的下凸壳上的点mid.
这里的特殊指的是要使L,R,mid组成的三角形面积最大,这样才能保证复杂度.
可以直接用向量叉积来算这个面积,那么我们就要最大化:
Lmid−→−−×LR−→=(R.y−L.y)(mid.x−L.x)−(R.x−L.x)(mid.y−L.y).
去掉常数后即最小化(好吧是我推的时候脑残搞成了最小化,反正都一样):
(L.y−R.y)∑bi+(R.x−L.x)∑ci.
这个东西可以改b,c的系数然后01背包搞吧…
求出mid之后再递归去求解两个子区间即可.
注意最开始要先找到两边的点,即求∑bi最小和∑ci最小的点.
然而这种做法的复杂度是多少?奥妙重重呀.
其实我之前还做过一道类似的好题:HNOI2014 画框.
还有一个经典问题用的也是这个思路:最小乘积生成树.
依旧按照我做题的顺序给出题解.
题目链接
1003 瞬间移动
我的做法比较简单粗暴,枚举步数然后用组合数求.复杂度是O(n)的.很多题解都说可以打表找出规律之后O(1)用组合数计算,奥妙重重呀.
1006 中位数计数
首先看清楚题目条件,n个数都是独一无二的,于是就只用考虑长度为奇数的区间了.暴力O(n2)即可.1005 区间交
首先区间的交的左端点是所有左端点的最大值,右端点是所有右端点的最小值.又因为数字都是非负的,所以在左端点确定的情况下右端点越往右越好.这个贪心还是比较显然的.具体怎么实现,可以用Splay来维护,但还有一种更机智的用堆的做法,先将区间双关键字排序,考虑当前区间的L作为最终区间的交的L,从前面的区间中取出第k−1大的R值,这可以用一个小顶堆维护k−1个最大值来实现.注意k=1的情况.
这样做的复杂度是O(nlgn).
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int N=1e5+5; int n,K,m,num ; typedef __int64 ll; ll sum ; typedef pair<int,int> P; priority_queue<int,vector<int>,greater<int> >pque; P itv ; void rd(int &res){ res=0; char c; while(c=getchar(),c<48); do res=(res<<3)+(res<<1)+(c^48); while(c=getchar(),c>47); } inline void Max(ll &a,ll b){ if(b>a)a=b; } inline void Min(int &a,int b){ if(b<a)a=b; } void solve(){ sum[0]=0; for(int i=1;i<=n;++i){ rd(num[i]); sum[i]=sum[i-1]+num[i]; } for(int i=0;i<m;++i){ rd(itv[i].first); rd(itv[i].second); } ll ans=0; if(K==1){ for(int i=0;i<m;++i) Max(ans,sum[itv[i].second]-sum[itv[i].first-1]); printf("%I64d\n",ans); return; } sort(itv,itv+m); while(!pque.empty())pque.pop(); for(int i=0;i<K-1;i++) pque.push(itv[i].second); for(int i=K-1;i<m;++i){ int L=itv[i].first,R=itv[i].second, elm=pque.top(); pque.pop(); Min(R,elm); if(L<=R)Max(ans,sum[R]-sum[L-1]); pque.push(max(elm,itv[i].second)); } printf("%I64d\n",ans); } int main(){ while(~scanf("%d%d%d",&n,&K,&m))solve(); return 0; } /* May.26.16 Tags:Greedy,Data Structure Submissions:1 Exe.Time 1606MS Exe.Memory 4360K Code Len. 1344B */
1001 区间的价值
这题比赛时到最后也没交过…题目中很关键的条件是数据随机,而这种题目我几乎没有做过…连个水分的方法都没有想出来…
赛后看到很多人的做法都是找到区间最大值,更新答案后递归到两个子区间,这样做期望是O(nlgn)的,实现并没有什么技术含量.
ShinFeb有并查集+线段树的O(nlgn)玄学做法.
1004 货物运输
这题第一个想法肯定是二分答案,然后对于每个区间,如果建立的传送站为(x,y)的话,满足条件的(x,y)组成的是一个对角线与坐标轴平行的正方形区域,只要求所有这些区域交集是否为空即可.然而我并不会正方形求交….>_<
好吧其实这已经几乎是正解了.
设二分的答案为len,那么对每个R−L>len的区间[L,R],需满足|L−x|+|R−y|⩽len,这等价于
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪L−x+R−y⩽lenL−x−R+y⩽len−L+x+R−y⩽len−L+x−R+y⩽len
然后化为y关于x的四条不等式,这下判定有无解就方便了.
#include<cstdio> #include<algorithm> using namespace std; const int N=1e6+5,INF=1e9; int n,m,dat [2]; inline void Max(int &a,int b){ if(b>a)a=b; } inline void Min(int &a,int b){ if(b<a)a=b; } bool judge(int len){ int mi0=-INF,mx0=INF,mi1=-INF,mx1=INF; for(int i=0;i<m;++i){ int L=dat[i][0],R=dat[i][1]; if(R-L<=len)continue; Max(mi0,L+R-len); Min(mx0,L+R+len); Max(mi1,-L+R-len); Min(mx1,-L+R+len); } return mi0<=mx0&&mi1<=mx1; } void rd(int &res){ res=0; char c; while(c=getchar(),c<48); do res=(res<<3)+(res<<1)+(c^48); while(c=getchar(),c>47); } int main(){ while(~scanf("%d",&n)){ rd(m); for(int i=0;i<m;++i){ for(int j=0;j<2;++j) rd(dat[i][j]); if(dat[i][0]>dat[i][1])swap(dat[i][0],dat[i][1]); } int L=0,R=n,ans; while(L<=R){ int mid=L+R>>1; if(judge(mid)){ ans=mid; R=mid-1; } else L=mid+1; } printf("%d\n",ans); } return 0; }
1002 刷题计划
一道蛮好的数形结合+01背包题.然而这里我懒得画图了…>_<
把所有满足条件的方案看成平面上的点,横坐标为选出的bi之和,纵坐标为选出的ci之和.
因为要使横纵坐标的乘积最小,所以最优解肯定在所谓的”下凸壳”上.
现在考虑如果已知满足条件的下凸壳上的两个点L,R,如何求一个横坐标在两者之间的下凸壳上的点mid.
这里的特殊指的是要使L,R,mid组成的三角形面积最大,这样才能保证复杂度.
可以直接用向量叉积来算这个面积,那么我们就要最大化:
Lmid−→−−×LR−→=(R.y−L.y)(mid.x−L.x)−(R.x−L.x)(mid.y−L.y).
去掉常数后即最小化(好吧是我推的时候脑残搞成了最小化,反正都一样):
(L.y−R.y)∑bi+(R.x−L.x)∑ci.
这个东西可以改b,c的系数然后01背包搞吧…
求出mid之后再递归去求解两个子区间即可.
注意最开始要先找到两边的点,即求∑bi最小和∑ci最小的点.
然而这种做法的复杂度是多少?奥妙重重呀.
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=405,SIGMA=805; int n,m,sum_A,A ,B ,C ; typedef __int64 ll; const ll INF=1ll<<60; ll ans,val[SIGMA]; struct Point{ ll x,y; inline bool operator ==(const Point &tmp)const{ return x==tmp.x&&y==tmp.y; } }dp[SIGMA]; inline void Min(ll &a,ll b){ if(b<a)a=b; } Point DP(ll kb,ll kc){ dp[0]=(Point){0,0}; val[0]=0; for(int i=1;i<=sum_A;++i){ dp[i]=(Point){INF,INF}; val[i]=INF; } for(int i=0;i<n;++i){ for(int j=sum_A;j>=A[i];--j){ ll value=val[j-A[i]]+kb*B[i]+kc*C[i]; if(value<val[j]){ val[j]=value; dp[j]=(Point){dp[j-A[i]].x+B[i],dp[j-A[i]].y+C[i]}; } } } int res_id=m; Point res=dp[m]; for(int i=m+1;i<=sum_A;++i){ if(val[i]<val[res_id]){ res_id=i; res=dp[i]; } } return res; } void rec(Point L,Point R){ Point mid=DP(L.y-R.y,R.x-L.x); if(mid==L||mid==R)return; Min(ans,mid.x*mid.y); rec(L,mid); rec(mid,R); } int main(){ while(~scanf("%d%d",&n,&m)){ sum_A=0; for(int i=0;i<n;++i){ scanf("%d%d%d",&A[i],&B[i],&C[i]); sum_A+=A[i]; } Point L=DP(1,0),R=DP(0,1); ans=min(L.x*L.y,R.x*R.y); rec(L,R); printf("%I64d\n",ans); } return 0; } /* May.26.16 Tags:Mathematics,dp Submissions:1 Exe.Time 826MS Exe.Memory 1424K Code Len. 1266B */
其实我之前还做过一道类似的好题:HNOI2014 画框.
还有一个经典问题用的也是这个思路:最小乘积生成树.
相关文章推荐
- 2014武大邀请赛总结
- 2014 百度之星资格赛 1001 Energy Conversion
- 百度之星2014 资格赛 1001 Energy Conversion
- 百度之星2016 资格赛 java 代码实现
- 2014百度之星资格赛1004 度度熊走迷宫 Labyrinth
- 2014百度之星1001
- 没参加的2015百度之星——数矩形
- 没参加的2015百度之星——找连续数
- 【百度之星】1003 IP聚合
- 2016百度之星资格赛D题
- HDU 5698 瞬间移动
- HDU 5690 All X
- HDU 5694 BD String
- HDU 5701 中位数计数
- BestCoder 2015百度之星资格赛1003 IP聚合 -
- 逆元的使用
- 华为软件精英赛总结
- 2016"百度之星" - 资格赛(Astar Round1)
- 2016"百度之星" - 初赛(Astar Round2A)
- 百度之星2016初赛