APIO2009 抢掠计划 Tarjan DAG-DP

APIO2009 抢掠计划 Tarjan DAG-DP

题面

一道\(Tarjan\)缩点水题。因为可以反复经过节点,所以把一个联通快中的所有路口看做一个整体,缩点后直接跑spfa或者dp就好了。

我选择了在DAG上跑dp,毕竟复杂度\(O(n)\)

拓扑时搞DP,\(f[i]\)表示在DAG上\(i\)节点时,当前最大钱数,转移\(f[v]=max(f[v], f[u]+w[v])?\)

#include <cstdio>
#include <queue>
#define MAXN 500005
#define MIN(A,B) ((A)<(B)?(A):(B))
#define MAX(A,B) ((A)>(B)?(A):(B))
using namespace std;
int n,m,sta,p;
bool hav[MAXN],col_hav[MAXN];
int head[MAXN],nxt[MAXN],vv[MAXN],tot;
inline void add_edge(int u, int v){
    vv[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
int s[MAXN],top;
bool ins[MAXN];
int col[MAXN],col_cnt;
int val[MAXN],col_val[MAXN];
int low[MAXN],dfn[MAXN],cnt;
void tarjan(int u){
    dfn[u]=++cnt;
    low[u]=cnt;
    s[++top]=u;
    ins[u]=1;
    for(register int i=head[u];i;i=nxt[i]){
        int v=vv[i];
        if(dfn[v]==0){
            tarjan(v);
            low[u]=MIN(low[u], low[v]);
        }else if(ins[v]){
            low[u]=MIN(low[u], dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        col[u]=++col_cnt;
        ins[u]=0;
        col_val[col_cnt]=val[u];
        while(s[top]!=u){
            col[s[top]]=col_cnt;
            ins[s[top]]=0;
            col_val[col_cnt]+=val[s[top]];
            top--;
        }
        top--;
    }
}
int head2[MAXN],nxt2[MAXN],vv2[MAXN],tot2;
inline void add_edge2(int u, int v){
    vv2[++tot2]=v;
    nxt2[tot2]=head2[u];
    head2[u]=tot2;
}
int rdu[MAXN];
inline void build(){
    for(register int u=1;u<=n;++u)
    if(dfn[u]!=0){
        for(register int i=head[u];i;i=nxt[i]){
            int v=vv[i];
            if(col[u]==col[v]) continue;
            rdu[col[v]]++;
            add_edge2(col[u], col[v]);
        }
    }
}
queue <int> q;
int f[MAXN],ans=0;
void dp(){
    q.push(col[sta]);f[col[sta]]=col_val[col[sta]];
    while(!q.empty()){
        int u=q.front();q.pop();
        for(register int i=head2[u];i;i=nxt2[i]){
            int v=vv2[i];
            f[v]=MAX(f[v], f[u]+col_val[v]);
            if((--rdu[v])==0) q.push(v);
        }
    }
    for(register int i=1;i<=col_cnt;++i)
        if(col_hav[i]) ans=MAX(f[i], ans);
}
int main()
{
    scanf("%d %d", &n, &m);
    while(m--){
        int a,b;scanf("%d %d", &a, &b);
        add_edge(a,b);
    }
    for(register int i=1;i<=n;++i) scanf("%d", &val[i]);
    scanf("%d %d", &sta, &p);
    while(p--){
        int x;scanf("%d", &x);
        hav[x]=1;
    }
    tarjan(sta);
    for(register int i=1;i<=n;++i)
        if(hav[i]) col_hav[col[i]]=1;
    build();
    dp();
    printf("%d", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/santiego/p/10943425.html

时间: 2024-10-11 12:14:29

APIO2009 抢掠计划 Tarjan DAG-DP的相关文章

【p3627】[APIO2009] 抢掠计划

Description Siruseri 城中的道路都是单向的.不同的道路由路口连接.按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机.令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧. Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫.他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利. 使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金

[APIO2009]抢掠计划

题目描述 Siruseri 城中的道路都是单向的.不同的道路由路口连接.按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机.令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧. Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫.他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利. 使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额.他希 望

【bzoj1179】[Apio2009]抢掠计划atm 强连通分量缩点+spfa

Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数. Sample

洛谷3627 [APIO2009]抢掠计划

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

BZOJ 2595 游览计划(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2595 题意:给出一个数字矩阵.求一个连通块包含所有的数字0且连通块内所有数字之和最小. 思路:对于每个格子,是0则必须要选.那么对于不选的格子(i,j)在什么时候可以不选呢?必须同时满足以下两个条件: (1)(i,j)不是0: (2)(i-1,j)不选或者(i-1,j)选了但是轮廓线上还有别的地方与(i-1,j)是一个连通块. int Pre[105][N],op[105][N]; s

poj3249 Test for job 【图的DAG dp】

#include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <stack> #include <vector> using namespace std; const int MAX=111111; int N,E; int v[

UVa11324 - The Largest Clique(DAG+DP+SCC)

Problem B: The Largest Clique Given a directed graph G, consider the following transformation. First, create a new graph T(G) to have the same vertex set as G. Create a directed edge between two vertices u and v in T(G) if and only if there is a path

[SDOI2010] 所驼门王的宝藏 [建图+tarjan缩点+DAG dp]

题面传送门: 传送门 思路: 看完题建模,容易得出是求单向图最长路径的问题 那么把这张图缩强联通分量,再在DAG上面DP即可 然而 这道题的建图实际上才是真正的考点 如果对于每一个点都直接连边到它所有的后继节点,那么可以被卡掉(1e5个点在同一行上) 考虑改变思路,运用网络流建图中的一个常用技巧:把横边和竖边映射成点,再从每个点向所在横坐标.纵坐标代表的点连边即可 这样会有2e6+1e5个点,但是tarjan算法效率O(n),完全无压力 自由(和谐)门的话,目前还没有比较好的方法解决 上网看了一

[BZOJ1179] [Apio2009]Atm(tarjan缩点 + spfa)

传送门 题意 N个点M条边的有向图 每个点有点权 从某一个结点出发 问能获得的最大点权和 一个点的点权最多被计算一次 N<=500000 M<=500000 思路 先tarjan缩点,然后就形成一个dag,无环,所以直接spfa求最长路就行. 也可以先缩点,然后拓扑排序 + dp 搞. 代码 1 #include <map> 2 #include <queue> 3 #include <stack> 4 #include <cstdio> 5 #