话说这题非常恶心,我整整写了上午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]<t do inc(l);
while nb[r]>t 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>=0) do 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
一芥书生 阅读(978)
评论(2) 编辑 收藏 引用