Holedox Eating
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1187 Accepted Submission(s): 391
The next n lines, each line describes an event. 0 x(0<=x<=L, x is a integer) represents a piece of cake appears in the x position; 1 represent Holedox wants to eat a cake.
In each case, Holedox always starts off at the position 0.
3 10 8 0 1 0 5 1 0 2 0 0 1 1 1 10 7 0 1 0 5 1 0 2 0 0 1 1 10 8 0 1 0 1 0 5 1 0 2 0 0 1 1
Case 1: 9 Case 2: 4 Case 3: 2
这道题神奇得可以用三种方法,线段树容易想到但难敲,堆相对容易,multiset这种奇淫巧计竟然也可以过:
线段树:
#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<queue>
#include<algorithm>
using namespace std;
priority_queue<int,vector<int>,greater<int> > minQue;
priority_queue<int> maxQue;
int main(){
long long res;
int k,l,n,left,targ,val,right,curr,dir;
while(scanf("%d",&k)!=EOF){
for(int i=1;i<=k;i++){
while(!minQue.empty()) minQue.pop();
while(!maxQue.empty()) maxQue.pop();
res=0;
curr=0;
dir=1;
scanf("%d%d",&l,&n);
while(n--){
scanf("%d",&targ);
if(targ){
if(minQue.empty()){
if(!maxQue.empty()){
left=maxQue.top();
maxQue.pop();
res+=curr-left;
if(curr!=left) dir=0;
curr=left;
}
}
else{
if(maxQue.empty()){
right=minQue.top();
minQue.pop();
res+=right-curr;
curr=right;
dir=1;
}
else{
left=maxQue.top();
right=minQue.top();
if(curr-left<right-curr){
res+=curr-left;
if(curr!=left) dir=0;
curr=left;
maxQue.pop();
}
else if(curr-left>right-curr){
res+=right-curr;
dir=1;
curr=right;
minQue.pop();
}
else{
if(dir){
res+=right-curr;
dir=1;
curr=right;
minQue.pop();
}
else{
res+=curr-left;
if(curr!=left) dir=0;
curr=left;
maxQue.pop();
}
}
}
}
}
else{
scanf("%d",&val);
if(val<=curr) maxQue.push(val);
else minQue.push(val);
}
}
printf("Case %d: ",i);
cout<<res<<endl;
}
}
return 0;
}
multiset:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#include<cmath>
using namespace std;
int main(){
int t;
long long sum;
int L,n;
int a,b;
long long Min,p;
scanf("%d",&t);
for(int Case=1;Case<=t;Case++){
scanf("%d%d",&L,&n);
sum=0;
multiset<int>s; //multiset用红黑树来组织元素数据,题目中需要允许重复插入元素键值,故不用set
multiset<int>::iterator It,del;
int cur=0,pre=0;
while(n--){
scanf("%d",&a);
if(a==0){
scanf("%d",&b);
s.insert(b);
}
else{
Min=0x3fffffff;
if(!s.size())
continue;
for(It=s.begin();It!=s.end();It++){
if(abs(*It-cur)<Min){
Min=abs((*It)-cur);
p=(*It); //得到距离最近的那个蛋糕的位置
del=It; //标记要吃的蛋糕,以便于删除
}
else if(abs(*It-cur)==Min){ //当距离一样时取同方向的
if((*It-cur)*(cur-pre)>0){
Min=abs((*It)-cur);
p=(*It);
del=It;
}
}
}
s.erase(del);
sum+=Min;
pre=cur; //之前所在的位置
cur=p; //目前的位置
}
}
printf("Case %d: %I64d\n",Case,sum);
}
// system("pause");
return 0;
}
163



被折叠的 条评论
为什么被折叠?



