农场分配(USACO2010_balloc)

本文探讨了如何运用线段树和贪心算法解决动态修改区间问题,通过实例解析实现步骤,并展示了快排在算法优化过程中的作用。
算法:线段树+贪心

老朋友又见面了,这次又没认出他来。。。
读完整个代码以后,发现这道题目还是比较容易理解的,想到要动态修改区间了但是没有多想。
用线段树的目的是求最小值,因为如果满足一个要求的话就看能否满足这个最小值的要求了,如果最小的都满足不了那么就无法满足这个奶牛的要求了。
tree记录值的时候需要记录两个值,一个是当前区间的最小值,一个是向下进行修改的值。

快排的目的是分出一个段来,能保证左右两边都取最短的,既满足了我们要进行贪心的性质(即先选最短的进行满足),同时也保证了线段树分成左右两区间的性质。

program balloc;

const
 maxn=100000;

type
 tre=record
  l,r,lc,rc,data,mindata:longint;
 end;
 atp=record
  l,r:longint;
 end;

var
 n,m,tot,ans,root:longint;
 tree:array [0..maxn*4] of tre;
 tnt:array [0..maxn] of atp;
 a:array [0..maxn] of longint;

function min(x,y:longint):longint;
begin
 if x<y then exit(x) else exit(y);
end;

procedure build(var t:longint;l,r:longint);
var
 mid:longint;
begin
 inc(tot);
 t:=tot;
 tree[t].l:=l;
 tree[t].r:=r;
 if l=r then
  begin
   tree[t].mindata:=a[l];
   exit;
  end;
 mid:=(l+r) shr 1;
 build(tree[t].lc,l,mid);
 build(tree[t].rc,mid+1,r);
 tree[t].mindata:=min(tree[tree[t].lc].mindata,tree[tree[t].rc].mindata);
end;

procedure qsort(l,r:longint);
var
 i,j,mm,mid1,mid2:longint;
 t:atp;
begin
 i:=l;
 j:=r;
 mm:=(l+r) shr 1;
 mid1:=tnt[mm].r;
 mid2:=tnt[mm].l;
 repeat
  while (tnt[i].r<mid1) or ((tnt[i].r=mid1) and (tnt[i].l>mid2)) do inc(i);
  while (tnt[j].r>mid1) or ((tnt[j].r=mid1) and (tnt[j].r<mid2)) do dec(j);
  if i<=j then
   begin
    t:=tnt[i];
    tnt[i]:=tnt[j];
    tnt[j]:=t;
    inc(i);
    dec(j);
   end;
 until i>j;
 if i<r then qsort(i,r);
 if l<j then qsort(l,j);
end;

procedure init;
var
 i,mini:longint;
begin
 readln(n,m);
 for i:=1 to n do readln(a[i]);
 build(root,1,n);
 for i:=1 to m do readln(tnt[i].l,tnt[i].r);
 qsort(1,m);
end;

procedure pass(t,add:longint);
begin
 inc(tree[tree[t].lc].data,add);
 inc(tree[tree[t].rc].data,add);
 inc(tree[tree[t].lc].mindata,add);
 inc(tree[tree[t].rc].mindata,add);
 tree[t].data:=0;
end;

function get(l,r,t:longint):longint;
var
 ans1,ans2:longint;
begin
 if tree[t].data<>0 then pass(t,tree[t].data);
 if (tree[t].l=l) and (tree[t].r=r) then exit(tree[t].mindata);
 ans1:=maxlongint;
 ans2:=maxlongint;
 if r<=tree[tree[t].lc].r then ans1:=get(l,r,tree[t].lc)
 else if l>=tree[tree[t].rc].l then ans2:=get(l,r,tree[t].rc)
 else
  begin
   ans1:=get(l,tree[tree[t].lc].r,tree[t].lc);
   ans2:=get(tree[tree[t].rc].l,r,tree[t].rc);
  end;
 exit(min(ans1,ans2));
end;

procedure change(l,r,x,t:longint);
begin
 if (tree[t].l=l) and (tree[t].r=r) then
  begin
   inc(tree[t].data,x);
   inc(tree[t].mindata,x);
   exit;
  end;
 if tree[t].data<>0 then pass(t,tree[t].data);
 if r<=tree[tree[t].lc].r then change(l,r,x,tree[t].lc)
 else if l>=tree[tree[t].rc].l then change(l,r,x,tree[t].rc)
 else
  begin
   change(l,tree[tree[t].lc].r,x,tree[t].lc);
   change(tree[tree[t].rc].l,r,x,tree[t].rc);
  end;
 tree[t].mindata:=min(tree[tree[t].lc].mindata,tree[tree[t].rc].mindata);
end;

procedure main;
var
 i,mini:longint;
begin
 for i:=1 to m do
  begin
   mini:=get(tnt[i].l,tnt[i].r,root);
   if mini>0 then
    begin
     inc(ans);
     change(tnt[i].l,tnt[i].r,-1,root);
    end;
  end;
 writeln(ans);
end;

begin
 assign(input,'balloc.in'); reset(input);
 assign(output,'balloc.out'); rewrite(output);

 init;
 main;

 close(input); close(output);
end.


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值