http://acm.hdu.edu.cn/showproblem.php?pid=4302
给一个一维的线段L 小动物最初最0点
两种操作
0 x 在x位置添加一个吃的
1 小动物去吃一个距离它最近的一个吃的 左右距离相等的话选择上一个选择方向
这个题既可以用线段树 也可以用优先队列
用优先队列 代码短 好理解 效率高
但这题确实是一个练习线段树的好题
线段树代码及其注释:
#include<iostream> #include<stdio.h> #include<string.h> #include<queue> #include<cmath> #include<stack> #include<algorithm> using namespace std; const int N=100005; struct node { int l,r; int sum; }mem[N*3]; int num[N];//记录某个位置的食物数量 int place,suml,sumr;//小动物的位置 和它左边 右边的食物数量 int to;//表示方向 1 向右 0 向左 int ans;//保存答案 int L,R;//搜索左边和右边最靠近小动物的食物位置 int Search(int,int ); void insert(int ,int ,int ); void Right()//选择右边的食物 进行的一些必要更新 { to=1; sumr-=num[R]; insert(1,R,-num[R]); --num[R]; ans=ans+R-place; place=R; } void Left()//选择左边的食物 进行的一些必要更新 { to=0; suml-=num[L]; insert(1,L,-num[L]); --num[L]; ans=ans+place-L; place=L; } void build(int x,int i,int j)//建树 { mem[x].l=i; mem[x].r=j; mem[x].sum=0; if(i==j) return ; int mid=(i+j)>>1; build(x*2,i,mid); build(x*2+1,mid+1,j); } void insert(int x,int p,int k)//在p这个位置 插入k个食物 k可以为负 用来减少操作 { int mid=(mem[x].l+mem[x].r)>>1; if(mem[x].l==mem[x].r) { mem[x].sum+=k; return ; } if(p<=mid) insert(x*2,p,k); else insert(x*2+1,p,k); mem[x].sum=mem[x*2].sum+mem[x*2+1].sum; } int Search(int x,int d)//搜索第d个食物的位置 { if(mem[x].l==mem[x].r) return mem[x].r; if(mem[x*2].sum>=d) return Search(x*2,d); else return Search(x*2+1,d-mem[x*2].sum); } int main() { int T; scanf("%d",&T); for(int w=1;w<=T;++w) { int n,m; place=0,suml=0,sumr=0; to=1; ans=0; scanf("%d %d",&n,&m); build(1,0,n); memset(num,0,sizeof(num)); while(m--) { int k,x; scanf("%d",&k); if(k==0) { scanf("%d",&x); ++num[x]; if(x!=place)//插入位置 不是在小动物位置才更新线段树 insert(1,x,1); if(x<place) ++suml; else if(x>place) ++sumr; }else { if(num[place]>0)//在小动物位置 直接减少 不需其他操作 { --num[place]; continue; } if(suml==0&&sumr==0)//没有食物 continue; if(sumr==0)//右边没食物 选左边的 { L=Search(1,suml); Left(); }else if(suml==0)//选右边的 { R=Search(1,1); Right(); }else if(suml>0&&sumr>0) { L=Search(1,suml);//求的左边最近食物位置 R=Search(1,suml+1);//求的右边最近食物位置 if(place-L<R-place||(place-L==R-place&&to==0)) Left(); else Right(); } } } printf("Case %d: %d\n",w,ans); } return 0; }
优先队列就比较简单了
代码:
include<iostream> #include<stdio.h> #include<string.h> #include<queue> #include<algorithm> using namespace std; const int N=100005; priority_queue< int >l; priority_queue< int >r; int sum[N]; int main() { int T; scanf("%d",&T); for(int w=1;w<=T;++w) { int n,m; scanf("%d %d",&n,&m); int place=0; int to=1; long long ans=0; while(!l.empty()) l.pop(); while(!r.empty()) r.pop(); l.push(-1); r.push(-(n+1)); memset(sum,0,sizeof(sum)); while(m--) { int k,x,L,R; scanf("%d",&k); if(k==0) { scanf("%d",&x); ++sum[x]; if(x<place) { if(sum[x]==1) l.push(x); }else if(x>place) { if(sum[x]==1) r.push(-x); } }else { //cout<<place<<" "<<sum[place]<<endl; //cout<<l.top()<<" "<<-r.top()<<endl; if(sum[place]>0) { --sum[place]; }else { L=l.top(); R=-(r.top()); if(L==-1&&R==(n+1)) continue; if(L!=-1&&R!=(n+1)) { if(place-L<R-place||(place-L==R-place&&to==0)) { to=0; ans=ans+place-L; place=L; --sum[L]; l.pop(); }else { to=1; ans=ans+R-place; place=R; --sum[R]; r.pop(); } }else { if(L==-1) { to=1; ans=ans+R-place; place=R; --sum[R]; r.pop(); }else { to=0; ans=ans+place-L; place=L; --sum[L]; l.pop(); } } } } } printf("Case %d: ",w); cout<<ans<<endl; } return 0; }