您的位置:首页 > 其它

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

2017-04-27 08:04 316 查看
题中说每个点连出一条边,所以图应该是很多个基环+外向树。

对于每一个联通块,一条最长路径可能在某一棵子树中,也可能两棵子树各一部分加上中间一段环的路径。所以先找出它的环,对每棵树先进行树型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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: