2013-09-15 20:04
题目描述
有这样一个游戏,桌面上摆了N枚硬币,分别标号1-N,每枚硬币有一个分数C[i]与一个后继硬币T[i]。作为游戏参与者的你,可以购买一个名为mlj的小机器人,从任一个硬币处开始游戏,然后跳往该硬币的后继硬币T[i],直到你要它停下来,经过每个硬币时,你可以选择是否捡起它。当某个mlj机器人停下来后将被扔掉,这时你可以选择结束游戏或再买一个mlj机器人继续游戏。
注意,每个硬币只能捡一次,而且你不能要求mlj跳向一个已被捡起的硬币或从一个已被捡起的硬币处开始游戏,因为那样会把mlj摔坏的。
Your Task
一开始你的得分是0,每购买一个mlj机器人将扣掉你M分,捡起一个硬币将得到对应的分数C[i],请问如何使得分尽量高(游戏过程中分数可以为负)。
输入文件
第一行两个正整数 N M
接下来N行,每行两个正整数C[i] T[i]。
输出文件
一个整数,最大得分。
样例输入
4 2
1 3
2 3
1 4
1 3
样例输出
2
数据约定
30% N<=10
60% N<=300
100 N<=100000 1<=T[i]<=N
运算过程及结果均在Longint范围内
因为有N个点,N条边,且每个点都只有一个后继,所以可推知图中一定存在环,所以先用tarjan缩点,得到一颗上宽下窄的树(因为一个点只能有一个后继,而每个点可以成为好多点的后继),为了DP方便,缩点重新建图时,将边反向,这时得到了一颗多叉树,考虑到可能出现森林,所以用一个总根节点将每颗多叉树的根节点连接起来。
然后我们得到了一颗多叉树,问题转化成了树形DP,由题意可知,因为到一个硬币可以不捡,所以机器人的路径可以重合,那么设W(X)代表从X节点向下走可以取得的最大值,假设X有多个儿子,因为当前有一个机器人由上方走来到X节点,所以X节点的儿子中最大的不用X重新买机器人,剩下的儿子中,如果W(P)>M,就相当于在P儿子处再买一个机器人,那么更新W(X)值,W(X):=W(P)-M;
{ $m 500000000 } // By BLADEVIL var n, m :longint; father : array [ 0 .. 200010 ] of longint; start :longint; flag, fseq : array [ 0 .. 100010 ] of boolean; stack : array [ 0 .. 100010 ] of longint; tot :longint; time :longint; low, dfn : array [ 0 .. 100010 ] of longint; key : array [ 0 .. 100010 ] of longint; color :longint; pre, last, other : array [ 0 .. 200010 ] of longint; l :longint; mark : array [ 0 .. 200010 ] of longint; ans :longint; function min(a,b:longint):longint; begin if a>b then min:=b else min:= a; end ; procedure connect(x,y:longint); begin inc(l); pre[l]: = last[x]; last[x]: = l; other[l]: = y; end ; procedure dfs(x:longint); var cur :longint; begin inc(tot); stack[tot]: = x; flag[x]: = true; fseq[x]: = true; inc(time); dfn[x]: = time; low[x]: = time; cur: = other[last[x]]; if not flag[cur] then begin dfs(cur); low[x]: = min(low[x],low[cur]); end else if fseq[cur] then low[x]:= min(low[x],dfn[cur]); cur: =- 1 ; if dfn[x]=low[x] then begin inc(color); while cur<>x do begin cur: = stack[tot]; dec(tot); fseq[cur]: = false; key[cur]: = color; mark[color]: =mark[color]+ mark[cur]; end ; end ; end ; procedure init; var i :longint; x :longint; p :longint; begin read(n,m); tot: = 0 ; color:= n; for i:= 1 to n do father[i]:= i; for i:= 1 to n do begin read(mark[i],x); connect(i,x); father[x]: = i; end ; for i:= 1 to n do if father[i]=i then start:= i; if start= 0 then inc(start); dfs(start); for i:= 1 to n do if key[i]= 0 then dfs(i); for i:= 1 to n do begin p: = other[last[i]]; if key[i]<>key[p] then begin connect(key[p],key[i]); father[key[i]]: = key[p]; end ; end ; for i:=n+ 1 to color do if father[i]= 0 then connect(color+ 1 ,i); end ; function w(x:longint):longint; var p, q :longint; i, j, maxx :longint; sum :longint; begin q: = last[x]; j: = 0 ; w: = 0 ; w: =w+ mark[x]; maxx: = 0 ; while q<> 0 do begin p: = other[q]; sum: = w(p); if sum>m then w:=w+sum- m; if sum>maxx then maxx:= sum; q: = pre[q]; end ; if maxx<m then w:=w+maxx else w:=w+ m; end ; begin assign(input, ' coin.in ' ); reset(input); assign(output, ' coin.out ' ); rewrite(output); init; ans: =w(color+ 1 )- m; if ans> 0 then writeln(ans) else writeln( 0 ); close(input); close(output); end .