[BZOJ1791][IOI2008] Island 基环外向树+DP

题中说每个点连出一条边,所以图应该是很多个基环+外向树。
对于每一个联通块,一条最长路径可能在某一棵子树中,也可能两棵子树各一部分加上中间一段环的路径。所以先找出它的环,对每棵树先进行树型DP,记到根最长距离为g[i]。然后把环展开(1-2-3的环展开成1-2-3-1-2),维护一个距离前缀和dis[i],这就是一个决策区间单调移动的DP了,方程为f[i]=max{g[j]-dis[j]}+dis[i] (i-j+1<=环长度),然后单调队列维护g[j]-dis[j]即可。
最终答案不一定是f[i]的最大值,也有可能在某一棵子树,所以树型DP的时候也要统计答案。
然后这题输入格式好,对第i行输入,i—>nt[i]的边,把i->nt[i]看成正向,nt[i]->i看成反向(正向的用nt数组记,反向的加到邻接表里),找环的时候只要随便找一点走正向边,树型dp的时候都是在反向边上dp,巧妙吧。。。
这题好像要手写栈,不然OJ上会RE,懒得写了。。。
(话说我本地测RE两个点,交上去OLE是什么鬼
代码:

type
  edge=^edgenode;
  edgenode=record
    t,w:longint;
    next:edge;
  end;
  node=record
    t:longint;
    w:int64;
  end;

const maxn=1000100;
var
  n,i,j,x,y,num,top,oncir:longint;
  ans,all:int64;
  con:array[0..maxn]of edge;
  visit,cir:array[0..maxn]of boolean;
  huan:array[0..maxn]of longint;
  nt:array[0..maxn]of node;
  g,dis:array[0..2*maxn]of int64;
  dl:array[0..2*maxn]of node;
procedure ins(x,y,w:longint);
var
  p:edge;
begin
  new(p);
  p^.t:=y;
  p^.w:=w;
  p^.next:=con[x];
  con[x]:=p;
end;
function max(x,y:int64):int64;
begin
  if x>y then exit(x)
  else exit(y);
end;

procedure findcir(v:longint);
var
  p:edge;
begin
  visit[v]:=true;
  if visit[nt[v].t]=false then findcir(nt[v].t)
  else oncir:=nt[v].t;
  if oncir>0 then begin inc(top); huan[top]:=v; cir[v]:=true; end;
  if oncir=v then oncir:=0;
  visit[v]:=false;
end;
function dfs(v:longint):int64;
var
  p:edge;
  now,o:int64;
begin
  p:=con[v];
  dfs:=0;
  now:=0;
  visit[v]:=true;
  while p<>nil do
  begin
    if cir[p^.t]=false then
    begin
      o:=dfs(p^.t)+p^.w;
      dfs:=max(dfs,o);
      ans:=max(ans,o+now);
      now:=max(now,o);
    end;
    p:=p^.next;
  end;
end;
procedure dp;
var
  i,j,head,tail:longint;
begin
  head:=1;
  tail:=0;
  for i:=1 to 2*top-1 do
  begin
    while (head<=tail)and(dl[head].t<=i-top) do inc(head);
    if i>=top then ans:=max(ans,dl[head].w+g[i]+dis[i]);
    while (head<=tail)and(g[i]-dis[i]>=dl[tail].w) do dec(tail);
    inc(tail);
    dl[tail].t:=i;
    dl[tail].w:=g[i]-dis[i];
  end;
end;
begin
  readln(n);
  for i:=1 to n do
  begin
    readln(x,y);
    nt[i].t:=x;
    nt[i].w:=y;
    ins(x,i,y);
  end;
  fillchar(visit,sizeof(visit),false);
  fillchar(cir,sizeof(cir),false);
  for i:=1 to n do
    if visit[i]=false then
    begin
      oncir:=0;
      top:=0;
      ans:=0;
      findcir(i);
      for j:=1 to top do
        g[j]:=dfs(huan[j]);
      for j:=1 to top-1 do
      begin
        g[j+top]:=g[j];
        huan[j+top]:=huan[j];
      end;
      dis[1]:=0;
      for j:=2 to 2*top-1 do
        dis[j]:=dis[j-1]+nt[huan[j]].w;
      dp;
      all:=all+ans;
    end;
  write(all);
end.

你可能感兴趣的:(dp,树)