【强连通分量·Tarjan】bzoj1179: [Apio2009]Atm

新博的第一发!

因为这几天切了几道强连通分量,所以从这里begin

【题目描述】

Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定,
在每个路口都设立了一个Siruseri 银行的ATM 取款机。令人奇怪的是,Siruseri
的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。
Banditji 计划实施Siruseri 有史以来最惊天动地的ATM 抢劫。他将从市中心
出发,沿着单向道路行驶,抢劫所有他途径的ATM 机,最终他将在一个酒吧庆
祝他的胜利。
使用高超的黑客技术,他获知了每个ATM 机中可以掠取的现金数额。他希
望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可
以经过同一路口或道路任意多次。但只要他抢劫过某个ATM 机后,该ATM 机
里面就不会再有钱了。
例如,假设该城中有6 个路口,道路的连接情况如下图所示:
市中心在路口1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表
示。每个ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢
劫的现金总数为47,实施的抢劫路线是:1-2-4-1-2-3-5。

【输入描述】

第一行包含两个整数N、M。N 表示路口的个数,M 表示道路条数。接下来
M 行,每行两个整数,这两个整数都在1 到N 之间,第i+1 行的两个整数表示第
i 条道路的起点和终点的路口编号。接下来N 行,每行一个整数,按顺序表示每
个路口处的ATM 机中的钱数。接下来一行包含两个整数S、P,S 表示市中心的
编号,也就是出发的路口。P 表示酒吧数目。接下来的一行中有P 个整数,表示
P 个有酒吧的路口的编号。

【输出描述】

输出一个整数,表示Banditji 从市中心开始到某个酒吧结束所能抢劫的最多
的现金总数。

【样例输入】

6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1 5
1 4
4 3 5 6

【样例输出】

47

【数据范围及提示】

50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM
机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心
沿着Siruseri 的单向的道路到达其中的至少一个酒吧。

题目链接在这里→http://www.lydsy.com/JudgeOnline/problem.php?id=1179
 

这是学oi以来刷的第一道强连通分量#interesting

看起来字好多#huaji

其实概括起来就是说从一个起点开始走走走,有若干个终点,并保证你肯定至少能到达其中一个,路径是单向的,可能有环,点上有权值,各点权只算一次,问起点到达给定的任意终点所得到的最大值。

所以只要tarjan把环缩点,点权迭代,得到一张新的图,根据点权刷最长路就好了

因为终点有多个要判下,还有一个方法就是取一个虚拟的终点t0,把所有给定终点向t0连边,最后的dist[t0]就是答案

一年前写得还是Pascal,刚开始看之前的代码发现建了双向边没反应过来,还一脸懵逼奥妙重重【捂脸】

Pascal代码看下面(当时题目一毛一样,不过范围被改成了50%的)

 const maxn=3010;maxv=6007;
 var n,m,s,p,num,top,time:longint;
     son,link,next,son1,link1,next1:array[0..2*maxn] of longint;
     money,w,low,dfn,father:array[0..maxn] of longint;
     que,stack:array[0..maxn] of longint;
     vis,instack:array[0..maxn] of boolean;
     dist:array[0..maxn] of longint;
 procedure add(x,y:longint);
  begin
   inc(num);son[num]:=y;next[num]:=link[x];link[x]:=num;
  end;
 procedure add1(x,y:longint);
  begin
   inc(num);son1[num]:=y;next1[num]:=link1[x];link1[x]:=num;
  end;
 procedure init;
  var i,x,y:longint;
   begin
    assign(input,'atm.in');reset(input);
    assign(output,'atm.out');rewrite(output);
    fillchar(son,sizeof(son),0);
    fillchar(link,sizeof(link),0);
    fillchar(next,sizeof(next),0);
    fillchar(money,sizeof(money),0);
    num:=0;
    readln(n,m);
    for i:=1 to m do
     begin
      readln(x,y);
      add(x,y);
     end;
    for i:=1 to n do readln(money[i]);
    readln(s,p);
    add(0,s);
    for i:=1 to p do
     begin
      read(x);
      add(x,n+1);
     end;
   end;
 function min(x,y:longint):longint;
  begin
   if x<y then exit(x) else exit(y);
  end;
 procedure swap(var x,y:longint);
  var t:longint;
   begin
    t:=x;x:=y;y:=t;
   end;
 procedure tarjan(x:longint);
  var j,y:longint;
   begin
    inc(time);dfn[x]:=time;low[x]:=time;
    instack[x]:=true;inc(top);stack[top]:=x;
    j:=link[x];
    while j<>0 do
     begin
      if dfn[son[j]]=0 then begin
                             tarjan(son[j]);
                             low[x]:=min(low[x],low[son[j]]);
                            end
                       else if instack[son[j]] then low[x]:=min(low[x],dfn[son[j]]);
      j:=next[j];
     end;
    if dfn[x]=low[x] then repeat
                           y:=stack[top];dec(top);
                           instack[y]:=false;
                           father[y]:=x;
                           until y=x;
   end;
 procedure spfa;
  var head,tail,j:longint;
   begin
    fillchar(vis,sizeof(vis),0);
    fillchar(que,sizeof(que),0);
    fillchar(dist,sizeof(dist),192);
    dist[father[0]]:=0;vis[father[0]]:=true;
    que[1]:=father[0];
    head:=0;tail:=1;
    while head<>tail do
     begin
      head:=(head+1) mod maxv;
      vis[que[head]]:=false;
      j:=link1[que[head]];
      while j<>0 do
       begin
        if dist[que[head]]+w[son1[j]] > dist[son1[j]] then
         begin
          dist[son1[j]]:=dist[que[head]]+w[son1[j]];
          if not vis[son1[j]] then begin
                                    vis[son1[j]]:=true;
                                    tail:=(tail+1) mod maxv;que[tail]:=son1[j];
                                    if dist[que[tail]]>dist[que[(head+1) mod maxv]] then swap(que[tail],que[(head+1) mod maxv]);
                                   end;
          end;
         j:=next1[j];
        end;
      end;
   end;
 procedure main;
  var i,j:longint;
   begin
    fillchar(low,sizeof(low),0);
    fillchar(dfn,sizeof(dfn),0);
    fillchar(stack,sizeof(stack),0);
    fillchar(father,sizeof(father),0);
    fillchar(instack,sizeof(instack),0);
    fillchar(w,sizeof(w),0);
    fillchar(son1,sizeof(son1),0);
    fillchar(link1,sizeof(link1),0);
    fillchar(next1,sizeof(next1),0);
    top:=0;time:=0;
    for i:=0 to n+1 do
     if dfn[i] = 0 then tarjan(i);
    num:=0;
    for i:=0 to n+1 do
     begin
      inc(w[father[i]],money[i]) ;
      j:=link[i];
      while j<>0 do
       begin
        if father[i]<>father[son[j]] then add1(father[i],father[son[j]]);
        j:=next[j];
       end;
     end;
     spfa;
   end;
 procedure print;
  begin
   writeln(dist[father[n+1]]);
   close(input);close(output);
  end;
 begin
  init;
  main;
  print;
 end.

下面一个是刚写的c++代码,有点懒,spfa没优化,后面也没向虚拟终点建边,代码看下面

#include<cstdio>
#include<cstring>
using namespace std;
const int NN=500005;
int son[NN],son0[NN],next[NN],next0[NN],link[NN],link0[NN],low[NN],dfn[NN],root[NN],s[NN],w[NN],que[NN],dist[NN],head,tail,n,m,tot,top,tot0,time,st;
bool vis[NN];
void add(int x,int y)
{
    son[++tot]=y;next[tot]=link[x];link[x]=tot;
}
void add0(int x,int y)
{
    son0[++tot0]=y;next0[tot0]=link0[x];link0[x]=tot0;
}
void tarjan(int x)
{
    int y,j;
    dfn[x]=low[x]=++time;
    s[++top]=x;
    vis[x]=true;
    j=link[x];
    while (j!=0)
    {
        y=son[j];
        if (dfn[y]==0)
        {
            tarjan(y);
            low[x]=low[x]<low[y]?low[x]:low[y];
        }
        else if (vis[y]&&dfn[y]<low[x]) low[x]=dfn[y];
        j=next[j];
    }
    if (low[x]==dfn[x])
    {
        do
        {
            y=s[top--];
            vis[y]=false;
            root[y]=x;
        }
        while (x!=y);
    }
}
void work()
{
    int x,j;
    time=top=0;
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(s,0,sizeof(s));
    for (int i=1;i<=n;i++)
    if (dfn[i]==0) tarjan(i);
    memset(link0,0,sizeof(link0));
    memset(w,0,sizeof(w));
    for (int i=1;i<=n;i++)
    {
        scanf("%d\n",&x);
        w[root[i]]+=x;
        j=link[i];
        while (j!=0)
        {
            x=son[j];
            if (root[i]!=root[x]) add0(root[i],root[x]);
            j=next[j];
        }
    }
}
void spfa()
{
    int x,j;
    memset(que,0,sizeof(que));
    memset(vis,0,sizeof(vis));
    memset(dist,0,sizeof(dist));
    head=0;tail=1;
    st=root[st];
    vis[st]=true;que[1]=st;dist[st]=w[st];
    while (head!=tail)
    {
        head=(head+1)% NN;
        x=que[head];
        vis[x]=false;
        j=link0[x];
        while (j!=0)
        {
            if (dist[x]+w[son0[j]]>dist[son0[j]])
            {
                dist[son0[j]]=dist[x]+w[son0[j]];
                if (! vis[son0[j]])
                {
                    vis[son0[j]]=true;
                    tail=(tail+1)%NN;
                    que[tail]=son0[j];
                }
            }
            j=next0[j];
        }
    }
}
int main()
{
    freopen("atm.in","r",stdin);
    freopen("atm.out","w",stdout);
    memset(link,0,sizeof(link));
    int x,y,p,ans;
    tot=0;tot0=0;
    scanf("%d%d\n",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d\n",&x,&y);
        add(x,y);
    }
    work();
    scanf("%d%d\n",&st,&p);
    spfa();
    ans=0;
    for (int i=1;i<=p;i++) 
    {
        scanf("%d",&x);
        ans=ans>dist[root[x]]?ans:dist[root[x]];
    }
    printf("%d",ans);
    return 0;
}

讲道理开头定义那里长长一串也真心受到了惊吓233

自恋下还是写得很优美的#huaji

【写的有漏洞的,欢迎路过大神吐槽】

2016-08-04 23:40:32

Ending.

posted @ 2016-08-04 23:40  白云千载空悠悠  阅读(401)  评论(2编辑  收藏  举报