您的位置:首页 > 其它

百度之星2016初赛第二场(Astar Round 2B)

2016-05-26 16:27 405 查看
这场比赛难度比起第一场有所提升.

依旧按照我做题的顺序给出题解.

题目链接

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 画框.

还有一个经典问题用的也是这个思路:最小乘积生成树.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息