寂寞花开
寂寞沙茶不知味
posts - 2,comments - 2,trackbacks - 0

话说这题非常恶心,我整整写了上午4节自习+三节晚自习
题目大意:
  给你一些线段,让你选出最多的不相交的线段,并且让这些线段的字典序最小。
题解:
  首先,如果是不要求字典序的话,应该很简单吧,按线段的结束时间从大到小排序, 然后贪心地求解。
  好,这个思想很重要,我们先用这个方法来求出最多的线段数num,然后按字典序一个一个往里放,如果这条线段放进去不与已经放进去的线段重叠并且能够构成最优解,那么就把它放进去,这个题的思想就是这样的。
  那么如果要知道它是不是与原来放进去的线段重叠很简单,只要用一棵平衡树或者线段树就可以了,我写的平衡树,那么如何判断它是否能构成最优解呢?这就是这道题的关键。
  先定义闭区间[l,r]内最多放的线段数量为find(l,r)
  每次判断一条线段能否能构成最优解时,先找到这条线段往左往右能到达的最大距离,记为l,r,然后,插入这条线段后,这段区间的最多放的线段数量=find(l,a[x]-1)+find(b[x]+1,r)+1。这个很显然吧,然后就是find的求法了。
  我们先把所有的线段按右端点升序排序,然后把所有包含小线段的大线段都删去,你会发现,连左端点也变成了升序的。这有什么好处呢,就是你以后再用这些线段搜的时候就是最优的了。
  定义f[i,j]表示从第i条线段开始取2^j条不相交的线段能达到的最近的端点。这个是不是跟解决rmq问题的st表很相似?对,就是用的st表的思想。同样,这个东西能够在nlogn的时间内解决。先预处理一个get[i]表示i这个点右边的第一条线段。
  然后f[i,j]=f[get[d[f[i,j-1]]+1],j-1],这样就能在nlogn内预处理出f数组了。
  这次find就应该知道怎样求了吧?枚举一个左端点i,不断减小j,看是否达到右端点,达到了就加到find上面,然后将i移动到f[i,j]后边,直到j=0为止。
  额,最后输出很简单。
  sui哥用的线段树但貌似没我快。。sbt果然神奇。
附程序,250+行:

program convention;
 var ctree
:array[0..200000] of record
                          size
,l,r,a,b:longint;
                          end;
     a
,b,c,d,id,pos:array[0..200000] of longint;
     nb
,get:array[0..400000] of longint;
     f
:array[0..200000,0..30] of longint;
     v
:array[0..200000] of boolean;
     n
,len,l,num,root,tot,fin:longint;
 {
-----------------------------------------}
 procedure qsort(x
,y:longint);
  var g
,t,s,l,r:longint;
  begin
   t
:=b[(x+y) div 2];l:=x;
   s
:=a[(x+y) div 2];r:=y;
   repeat
     
while (b[l]<t) or ((b[l]=t) and (a[l]>s)) do inc(l);
     
while (b[r]>t) or ((b[r]=t) and (a[r]<s)) do dec(r);
     
if l<=r then begin
     g
:=a[l];a[l]:=a[r];a[r]:=g;
     g
:=b[l];b[l]:=b[r];b[r]:=g;
     g
:=id[l];id[l]:=id[r];id[r]:=g;
     
pos[id[l]]:=l;pos[id[r]]:=r;
     inc(l);dec(r);
     end;
    
until l>r;
   
if l<y then qsort(l,y);
   
if x<r then qsort(x,r);
  end;
 {
-----------------------------------------}
 procedure qusort(x
,y:longint);
  var g
,t,l,r:longint;
  begin
   t
:=nb[(x+y) div 2];
   l
:=x;r:=y;
   repeat
     
while nb[l]<do inc(l);
     
while nb[r]>do dec(r);
     
if l<=r then begin
     g
:=nb[l];nb[l]:=nb[r];nb[r]:=g;
     inc(l);dec(r);
     end;
    
until l>r;
   
if l<y then qusort(l,y);
   
if x<r then qusort(x,r);
  end;
 {
-----------------------------------------}
 procedure change(var x
:longint);
  var l
,r,mid:longint;
  begin
   l
:=1;r:=len;
    repeat
     mid
:=(l+r) div 2;
     
if x>nb[mid] then l:=mid+1
              
else r:=mid-1;
     
until l>r;
    x
:=l;
  end;
 {
-----------------------------------------}
 procedure tl(var u
:longint);
  var t
:longint;
  begin
   t
:=ctree[u].r;
   ctree[u]
.r:=ctree[t].l;
   ctree[t]
.l:=u;
   ctree[t]
.size:=ctree[u].size;
   ctree[u]
.size:=ctree[ctree[u].l].size+ctree[ctree[u].r].size+1;
   u
:=t;
  end;
 {
-----------------------------------------}
 procedure tr(var u
:longint);
  var t
:longint;
  begin
   t
:=ctree[u].l;
   ctree[u]
.l:=ctree[t].r;
   ctree[t]
.r:=u;
   ctree[t]
.size:=ctree[u].size;
   ctree[u]
.size:=ctree[ctree[u].l].size+ctree[ctree[u].r].size+1;
   u
:=t;
  end;
 {
-----------------------------------------}
 procedure maintain(var u
:longint;flag:boolean);
  begin
   
if not flag then
    
if ctree[ctree[ctree[u].l].l].size>ctree[ctree[u].r].size 
     then tr(u)
     
else 
    
if ctree[ctree[ctree[u].l].r].size>ctree[ctree[u].r].size 
     then begin tl(ctree[u]
.l);tr(u); end
      
else exit
     
else
    
if ctree[ctree[ctree[u].r].r].size>ctree[ctree[u].l].size 
     then tl(u)
     
else
    
if ctree[ctree[ctree[u].r].l].size>ctree[ctree[u].l].size
     then begin tr(ctree[u]
.r);tl(u); end
     
else exit;
    maintain(ctree[u]
.l,false);
    maintain(ctree[u]
.r,true);
    maintain(u
,true);
    maintain(u
,false);
  end;
 {
-----------------------------------------}
 procedure makeget;
  var i
:longint;
  begin
   
for i:=1 to l do
    get[c[i]]
:=i;
   
for i:=c[l] downto 1 do
    
if get[i]=0 then get[i]:=get[i+1];
  end;
 {
-----------------------------------------}
 function find(x
,y:longint):longint;
  var i
,j:longint;
  begin
   
if x>=y then exit(0);
   find
:=0;
   i
:=get[x];
   j
:=fin;
   
while j>=0 do begin 
    
while ((f[i,j]=0) or (d[f[i,j]]>y)) and (j>=0do dec(j);
    
if j>=0 then
    inc(find
,1 shl j);
    i
:=f[f[i,j],1];
    end;
  end;
 {
-----------------------------------------}
 function cmp(x
,st,ed:longint):boolean;
  begin
   
if find(st,a[x]-1)+find(b[x]+1,ed)+1=find(st,ed)
    then 
exit(true)
    
else exit(false);
  end;
 {
-----------------------------------------}
 function check(x
,st,ed,u:longint):boolean;
  begin
   
if u=0 then 
    begin 
     
if cmp(x,st,ed) then exit(true) 
                          
else exit(false);
    end;
   
if a[x]>ctree[u].b then 
    begin
     check
:=check(x,ctree[u].b+1,ed,ctree[u].r);
     
exit;
    end 
else 
   
if b[x]<ctree[u].a then
    begin
     check
:=check(x,st,ctree[u].a-1,ctree[u].l);
     
exit;
    end 
else exit(false);
  end;
 {
-----------------------------------------}
 procedure insert(x
:longint; var u:longint);
  begin
   
if u=0 then 
    begin
     inc(tot);
     ctree[tot]
.size:=1;
     ctree[tot]
.a:=a[x];
     ctree[tot]
.b:=b[x];
     u
:=tot;
     
exit;
    end;
   
if a[x]>ctree[u].a then
    begin
     insert(x
,ctree[u].r);
     inc(ctree[u]
.size);
     maintain(u
,true);
    end 
else
    begin
     insert(x
,ctree[u].l);
     inc(ctree[u]
.size);
     maintain(u
,false);
     end;
  end;    
 {
-----------------------------------------}
 procedure init;
  var i
:longint;
  begin
   readln(n);
   
for i:=1 to n do begin
    readln(a[i]
,b[i]);
    
pos[i]:=i;id[i]:=i;
    inc(len);nb[len]
:=a[i];
    inc(len);nb[len]
:=b[i];
    end;
    qusort(
1,len);
    len
:=1;
    
for i:=1 to n*2 do
    
if nb[len]<>nb[i] then
     begin
      inc(len);nb[len]
:=nb[i];
     end;
    
for i:=1 to n do
     begin
      change(a[i]);change(b[i]);
     end;
  end;
 {
-----------------------------------------}
 procedure makef;
  var i
,j:longint;
  begin
   
for i:=1 to l do f[i,0]:=i;
   
for i:=1 to l do
    f[i
,1]:=f[get[d[f[i,0]]+1],0];
   
for j:=2 to fin do
    
for i:=1 to l-1 shl j+1 do
      f[i
,j]:=f[f[f[i,j-1],1],j-1];
  end;
 {
-----------------------------------------}
 procedure prepare;
  var i
,tmp:longint;
  begin
    root
:=0;
   qsort(
1,n);
   c[
1]:=a[1];d[1]:=b[1];
   l
:=1;i:=1;
  
for i:=1 to n do
    
if (a[i]>c[l]) and (b[i]>d[l]) then
    begin 
     inc(l);c[l]
:=a[i];d[l]:=b[i];
    end;
    fin
:=trunc(ln(l)/ln(2));
     makeget;
     makef;
    tmp
:=0;num:=0;
   
for i:=1 to l do
     
if c[i]>tmp then 
      begin inc(num);tmp
:=d[i];
      end;
  end;
 {
-----------------------------------------}
 procedure main;
  var i
:longint;
  begin
  prepare;
  
for i:=1 to n do 
    
if check(pos[i],1,len,root) then 
     begin 
      v[i]
:=true;
      insert(
pos[i],root);
     end;
   writeln(num);
  
for i:=1 to n do
   
if v[i] then write(i,' ');writeln;
 end;
 {
-----------------------------------------}
 begin
  assign(input
,'convention.in');reset(input);
  assign(output
,'convention.out');rewrite(output);
  init;
  main;
  
close(output);
 end
.

过两天要参加apio了,所以把近三年的apio都写完了,这道题是最恶心的一道。
如果有更好的做法欢迎指教。
posted on 2010-04-26 21:17 一芥书生 阅读(977) 评论(2)  编辑 收藏 引用

FeedBack:
# re: apio2009 convention
2010-05-08 11:36 | yzt
偶第一次去APIO
之前看题解花了半天还没搞懂 郁闷死了。。
还有1小时考试咯:) 好运  回复  更多评论
  
# re: apio2009 convention
2012-06-09 01:30 | lzqxh
其实可以用快速排序的思想做。。。虽然极端数据可能退化。。但是好写。。我C++这题就写了100行。。。数据最慢也只是1.0s+

把会议抽象成边就可以搞了  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理